pax_global_header00006660000000000000000000000064132040145000014477gustar00rootroot0000000000000052 comment=21fe94203b366bf43b5377bc131f4cbfa89ca80d cjs-3.6.1/000077500000000000000000000000001320401450000122655ustar00rootroot00000000000000cjs-3.6.1/.travis.yml000066400000000000000000000027741320401450000144100ustar00rootroot00000000000000language: 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-3.6.1/AUTHORS000066400000000000000000000000001320401450000133230ustar00rootroot00000000000000cjs-3.6.1/COPYING000066400000000000000000000023061320401450000133210ustar00rootroot00000000000000Copyright (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. 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-3.6.1/COPYING.LGPL000066400000000000000000000613141320401450000140620ustar00rootroot00000000000000 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-3.6.1/ChangeLog000066400000000000000000000000001320401450000140250ustar00rootroot00000000000000cjs-3.6.1/Makefile-examples.am000066400000000000000000000003611320401450000161350ustar00rootroot00000000000000EXTRA_DIST += \ examples/clutter.js \ examples/gio-cat.js \ examples/gtk.js \ examples/http-server.js \ examples/test.jpg cjs-3.6.1/Makefile-insttest.am000066400000000000000000000024701320401450000161770ustar00rootroot00000000000000EXTRA_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-3.6.1/Makefile-modules.am000066400000000000000000000035461320401450000157770ustar00rootroot00000000000000 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-3.6.1/Makefile-test.am000066400000000000000000000300551320401450000153010ustar00rootroot00000000000000EXTRA_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 $< mock_js_invalidation_resources_dir = $(top_srcdir)/test/gjs-test-coverage/cache_invalidation mock_js_invalidation_before_resources_files = \ $(mock_js_invalidation_resources_dir)/before/resource.js \ $(mock_js_invalidation_resources_dir)/before/mock-js-resource-cache-before.gresource.xml \ $(NULL) mock_js_invalidation_after_resources_files = \ $(mock_js_invalidation_resources_dir)/after/resource.js \ $(mock_js_invalidation_resources_dir)/after/mock-js-resource-cache-after.gresource.xml \ $(NULL) mock_js_invalidation_resources_files = \ $(mock_js_invalidation_before_resources_files) \ $(mock_js_invalidation_after_resources_files) \ $(NULL) mock-cache-invalidation-before.gresource: $(mock_js_invalidation_before_resources_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(mock_js_invalidation_resources_dir)/before $(mock_js_invalidation_resources_dir)/before/mock-js-resource-cache-before.gresource.xml mock-cache-invalidation-after.gresource: $(mock_js_invalidation_after_resources_files) $(AM_V_GEN) glib-compile-resources --target=$@ --sourcedir=$(mock_js_invalidation_resources_dir)/after $(mock_js_invalidation_resources_dir)/after/mock-js-resource-cache-after.gresource.xml 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) \ $(mock_js_invalidation_resources_files) \ $(srcdir)/test/mock-js-resources.gresource.xml \ $(srcdir)/test/gjs-test-coverage/loadedJSFromResource.js \ $(jsunit_resources_files) \ $(NULL) CLEANFILES += \ mock-cache-invalidation-before.gresource \ mock-cache-invalidation-after.gresource \ mock-js-resources.c \ jsunit-resources.c \ jsunit-resources.h \ $(NULL) ### TEST PROGRAMS ###################################################### # gjs-tests 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 minijasmine gjs_tests_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ $(GJSTESTS_CFLAGS) \ $(gjs_directory_defines) \ -I$(top_srcdir)/test gjs_tests_LDADD = \ libcjs.la \ $(GJSTESTS_LIBS) gjs_tests_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) gjs_tests_DEPENDENCIES = \ mock-cache-invalidation-before.gresource \ mock-cache-invalidation-after.gresource \ $(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 = -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/testClass.js \ installed-tests/js/testCoverage.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/testImporter.js \ installed-tests/js/testInterface.js \ installed-tests/js/testLang.js \ installed-tests/js/testLocale.js \ installed-tests/js/testMainloop.js \ installed-tests/js/testMetaClass.js \ installed-tests/js/testNamespace.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 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 \ $(NULL) ### TEST EXECUTION ##################################################### # 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; \ $(XVFB_START) \ $(NULL) simple_tests = \ installed-tests/scripts/testCommandLine.sh \ installed-tests/scripts/testWarnings.sh \ $(NULL) EXTRA_DIST += $(simple_tests) TESTS = \ gjs-tests \ $(simple_tests) \ $(jasmine_tests) \ $(NULL) TEST_EXTENSIONS = .js LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh JS_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh if DBUS_TESTS LOG_COMPILER = $(DBUS_RUN_SESSION) AM_LOG_FLAGS = --config-file=$(srcdir)/test/test-bus.conf -- $(top_srcdir)/test/run-test JS_LOG_COMPILER = $(DBUS_RUN_SESSION) AM_JS_LOG_FLAGS = --config-file=$(srcdir)/test/test-bus.conf -- $(top_builddir)/minijasmine else LOG_COMPILER = $(top_srcdir)/test/run-test AM_LOG_FLAGS = JS_LOG_COMPILER = $(top_builddir)/minijasmine AM_JS_LOG_FLAGS = endif !DBUS_TESTS if CODE_COVERAGE_ENABLED AM_TESTS_ENVIRONMENT += \ export GJS_UNIT_COVERAGE_OUTPUT=lcov; \ export GJS_UNIT_COVERAGE_PREFIX=resource:///org/gnome/gjs/; \ $(NULL) CODE_COVERAGE_IGNORE_PATTERN = */{include,mfbt,gjs/test,gjs/installed-tests}/* CODE_COVERAGE_GENHTML_OPTIONS = \ lcov/coverage.lcov \ --prefix $(abs_top_builddir)/lcov/org/gnome/gjs \ --prefix $(abs_top_builddir) \ $(NULL) @CODE_COVERAGE_RULES@ endif cjs-3.6.1/Makefile.am000066400000000000000000000130561320401450000143260ustar00rootroot00000000000000# 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_CXXFLAGS = $(WARN_CXXFLAGS) $(CODE_COVERAGE_CFLAGS) AM_CPPFLAGS = -DG_LOG_DOMAIN=\"Cjs\" $(CODE_COVERAGE_CPPFLAGS) AM_LDFLAGS = $(WARN_LDFLAGS) $(CODE_COVERAGE_LDFLAGS) 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 = \ -export-symbols-regex "^[^_]" -version-info 0:0:0 \ -no-undefined \ $(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) # 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 = -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/Class_Framework.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) -include $(top_srcdir)/git.mk cjs-3.6.1/NEWS000066400000000000000000000641661320401450000130010ustar00rootroot00000000000000Version 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-3.6.1/README000066400000000000000000000020631320401450000131460ustar00rootroot00000000000000JavaScript 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-3.6.1/README.md000066400000000000000000000021071320401450000135440ustar00rootroot00000000000000JavaScript 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-3.6.1/autogen.sh000077500000000000000000000015461320401450000142740ustar00rootroot00000000000000#!/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-3.6.1/cjs-1.0.pc.in000066400000000000000000000006151320401450000142730ustar00rootroot00000000000000prefix=@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-3.6.1/cjs/000077500000000000000000000000001320401450000130445ustar00rootroot00000000000000cjs-3.6.1/cjs/byteArray.cpp000066400000000000000000000570251320401450000155230ustar00rootroot00000000000000/* -*- 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, bool strict, JS::MutableHandleValue value_p); GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array); static void byte_array_finalize (JSFreeOp *fop, JSObject *obj); static JSObject *gjs_byte_array_get_proto(JSContext *); struct JSClass gjs_byte_array_class = { "ByteArray", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ (JSPropertyOp)byte_array_get_prop, (JSStrictPropertyOp)byte_array_set_prop, NULL, /* enumerate */ NULL, /* resolve */ NULL, /* convert */ byte_array_finalize }; 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 == NULL) 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 == NULL) 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 == NULL) 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) { 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 true; } /* 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, bool strict, JS::MutableHandleValue value_p) { ByteArrayInstance *priv; priv = priv_from_js(context, obj); if (priv == NULL) 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_set_index(context, obj, priv, idx, value_p); } /* We don't special-case anything else for now */ return true; } 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 == NULL) 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); char *encoding; bool encoding_is_utf8; gchar *data; if (priv == NULL) return true; /* prototype, not instance */ byte_array_ensure_array(priv); if (argc >= 1 && argv[0].isString()) { if (!gjs_string_to_utf8(context, argv[0], &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. */ if (strcmp(encoding, "UTF-8") == 0) { encoding_is_utf8 = true; g_free(encoding); encoding = NULL; } else { encoding_is_utf8 = false; } } 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(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); g_free(encoding); 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 == NULL) 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; char *encoding; bool encoding_is_utf8; JS::RootedObject obj(context, byte_array_new(context)); if (obj == NULL) 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()) { if (!gjs_string_to_utf8(context, argv[1], &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. */ if (strcmp(encoding, "UTF-8") == 0) { encoding_is_utf8 = true; g_free(encoding); encoding = NULL; } else { encoding_is_utf8 = false; } } else { encoding_is_utf8 = true; } if (encoding_is_utf8) { /* optimization? avoids iconv overhead and runs * libmozjs hardwired utf16-to-utf8. */ char *utf8 = NULL; if (!gjs_string_to_utf8(context, argv[0], &utf8)) return false; g_byte_array_set_size(priv->array, 0); g_byte_array_append(priv->array, (guint8*) utf8, strlen(utf8)); g_free(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); } } g_free(encoding); 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; JS::RootedObject obj(context, byte_array_new(context)); if (obj == NULL) 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)) { 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 == NULL) 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-3.6.1/cjs/byteArray.h000066400000000000000000000044521320401450000151640ustar00rootroot00000000000000/* -*- 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-3.6.1/cjs/cjs.stp.in000066400000000000000000000012331320401450000147570ustar00rootroot00000000000000 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-3.6.1/cjs/console.cpp000066400000000000000000000261141320401450000152160ustar00rootroot00000000000000/* -*- 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 *command = NULL; static gboolean print_version = false; static GOptionEntry entries[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, "Print GJS version 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" }, { 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 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; 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 }, { NULL } }; char **argv_copy = g_new(char *, argc + 2); int ix, argc_copy = argc + 1; 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(script_options, &argc_copy, &argv_copy, &error)) { g_warning("Scanning script arguments failed: %s", error->message); g_error_free(error); 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, argc_copy = argc, 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; 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(context, &argc_copy, &argv_copy, &error)) g_error("option parsing failed: %s", error->message); /* Split options so we pass unknown ones through to the JS script */ 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; g_option_context_set_ignore_unknown_options(context, false); g_option_context_set_help_enabled(context, true); if (!g_option_context_parse(context, &gjs_argc, &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 (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]; } 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); js_context = (GjsContext*) g_object_new(GJS_TYPE_CONTEXT, "search-path", include_path, "program-name", program_name, 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); } /* 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_strfreev(coverage_prefixes); if (coverage) g_object_unref(coverage); g_object_unref(js_context); g_free(script); exit(code); } cjs-3.6.1/cjs/context-private.h000066400000000000000000000034351320401450000163560ustar00rootroot00000000000000/* -*- 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" G_BEGIN_DECLS bool _gjs_context_destroying (GjsContext *js_context); void _gjs_context_schedule_gc_if_needed (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); G_END_DECLS #endif /* __GJS_CONTEXT_PRIVATE_H__ */ cjs-3.6.1/cjs/context.cpp000066400000000000000000000656721320401450000152540ustar00rootroot00000000000000/* -*- 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 "context-private.h" #include "importer.h" #include "jsapi-constructor-proxy.h" #include "jsapi-private.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "native.h" #include "byteArray.h" #include "runtime.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); struct _GjsContext { GObject parent; JSRuntime *runtime; JSContext *context; JS::Heap global; intptr_t owner_thread; char *program_name; char **search_path; bool destroying; bool should_exit; uint8_t exit_code; guint auto_gc_id; std::array const_strings; }; /* 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", "name", "x", "y", "width", "height", "__modulePath__" }; G_STATIC_ASSERT(G_N_ELEMENTS(const_strings) == GJS_STRING_LAST); struct _GjsContextClass { GObjectClass parent; }; G_DEFINE_TYPE(GjsContext, gjs_context, G_TYPE_OBJECT); enum { PROP_0, PROP_SEARCH_PATH, PROP_PROGRAM_NAME, }; static GMutex contexts_lock; static GList *all_contexts = NULL; static bool gjs_log(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); char *s; if (argc != 1) { gjs_throw(context, "Must pass a single argument to log()"); return false; } JS_BeginRequest(context); /* JS::ToString might throw, in which case we will only log that the value * could not be converted to string */ JS::AutoSaveExceptionState exc_state(context); JS::RootedString jstr(context, JS::ToString(context, argv[0])); exc_state.restore(); if (jstr == NULL) { g_message("JS LOG: "); JS_EndRequest(context); return true; } if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) { JS_EndRequest(context); return false; } g_message("JS LOG: %s", s); g_free(s); JS_EndRequest(context); argv.rval().setUndefined(); return true; } static bool gjs_log_error(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); if ((argc != 1 && argc != 2) || !argv[0].isObject()) { gjs_throw(context, "Must pass an exception and optionally a message to logError()"); return false; } JS_BeginRequest(context); JS::RootedString jstr(context); if (argc == 2) { /* JS::ToString might throw, in which case we will only log that the * value could be converted to string */ JS::AutoSaveExceptionState exc_state(context); jstr = JS::ToString(context, argv[1]); exc_state.restore(); } gjs_log_exception_full(context, argv[0], jstr); JS_EndRequest(context); argv.rval().setUndefined(); return true; } static bool gjs_print_parse_args(JSContext *context, JS::CallArgs &argv, char **buffer) { GString *str; gchar *s; guint n; JS_BeginRequest(context); 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(context); JS::RootedString jstr(context, JS::ToString(context, argv[n])); exc_state.restore(); if (jstr != NULL) { if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) { JS_EndRequest(context); g_string_free(str, true); return false; } g_string_append(str, s); g_free(s); if (n < (argv.length()-1)) g_string_append_c(str, ' '); } else { JS_EndRequest(context); *buffer = g_string_free(str, true); if (!*buffer) *buffer = g_strdup(""); return true; } } *buffer = g_string_free(str, false); JS_EndRequest(context); return true; } static bool gjs_print(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); char *buffer; if (!gjs_print_parse_args(context, argv, &buffer)) { return false; } g_print("%s\n", buffer); g_free(buffer); argv.rval().setUndefined(); return true; } static bool gjs_printerr(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); char *buffer; if (!gjs_print_parse_args(context, argv, &buffer)) { return false; } g_printerr("%s\n", buffer); g_free(buffer); argv.rval().setUndefined(); return true; } static void on_garbage_collect(JSRuntime *rt, 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(); } /* Requires request, does not throw error */ static bool gjs_define_promise_object(JSContext *cx, JS::HandleObject global) { /* This is not a regular import, we just load the module's code from the * GResource and evaluate it */ GError *error = NULL; GBytes *lie_bytes = g_resources_lookup_data("/org/cinnamon/cjs/modules/_lie.js", G_RESOURCE_LOOKUP_FLAGS_NONE, &error); if (lie_bytes == NULL) { g_critical("Failed to load Promise resource: %s", error->message); g_clear_error(&error); return false; } /* It should be OK to cast these bytes to const char *, since the module is * a text file and we setUTF8(true) below */ size_t lie_length; const char *lie_code = static_cast(g_bytes_get_data(lie_bytes, &lie_length)); JS::CompileOptions options(cx); options.setUTF8(true) .setSourceIsLazy(true) .setFile(""); JS::RootedValue promise(cx); if (!JS::Evaluate(cx, global, options, lie_code, lie_length, &promise)) { g_bytes_unref(lie_bytes); return false; } g_bytes_unref(lie_bytes); return JS_DefineProperty(cx, global, "Promise", promise, JSPROP_READONLY | JSPROP_PERMANENT); } 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); /* For CjsPrivate */ { #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_CallObjectTracer(trc, &gjs_context->global, "GJS global object"); } static void gjs_context_dispose(GObject *object) { GjsContext *js_context; js_context = GJS_CONTEXT(object); /* Run dispose notifications first, so that anything releasing * references in response to this can still get garbage collected */ G_OBJECT_CLASS(gjs_context_parent_class)->dispose(object); if (js_context->context != NULL) { gjs_debug(GJS_DEBUG_CONTEXT, "Destroying 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 */ JS_GC(js_context->runtime); JS_EndRequest(js_context->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_object_prepare_shutdown(js_context->context); if (js_context->auto_gc_id > 0) { g_source_remove (js_context->auto_gc_id); js_context->auto_gc_id = 0; } JS_RemoveExtraGCRootsTracer(js_context->runtime, gjs_context_tracer, js_context); js_context->global = NULL; for (auto& root : js_context->const_strings) delete root; /* Tear down JS */ JS_DestroyContext(js_context->context); js_context->context = NULL; g_clear_pointer(&js_context->runtime, gjs_runtime_unref); } } 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(); G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object); } static JSFunctionSpec global_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 }; 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->runtime = gjs_runtime_ref(); JS_AbortIfWrongThread(js_context->runtime); js_context->owner_thread = JS_GetCurrentThread(); js_context->context = JS_NewContext(js_context->runtime, 8192 /* stack chunk size */); if (js_context->context == NULL) g_error("Failed to create javascript context"); for (i = 0; i < GJS_STRING_LAST; i++) { js_context->const_strings[i] = new JS::PersistentRootedId(js_context->context, gjs_intern_string_to_id(js_context->context, const_strings[i])); } JS_BeginRequest(js_context->context); JS_SetGCCallback(js_context->runtime, on_garbage_collect, js_context); /* set ourselves as the private data */ JS_SetContextPrivate(js_context->context, js_context); JS::RootedObject global(js_context->context); if (!gjs_init_context_standard(js_context->context, &global)) g_error("Failed to initialize context"); JSAutoCompartment ac(js_context->context, global); if (!JS_DefineProperty(js_context->context, global, "window", global, JSPROP_READONLY | JSPROP_PERMANENT)) g_error("No memory to export global object as 'window'"); if (!JS_DefineFunctions(js_context->context, global, &global_funcs[0])) g_error("Failed to define properties on the global object"); new (&js_context->global) JS::Heap(global); JS_AddExtraGCRootsTracer(js_context->runtime, gjs_context_tracer, js_context); gjs_define_constructor_proxy_factory(js_context->context); /* We create the global-to-runtime root importer with the * passed-in search path. If someone else already created * the root importer, this is a no-op. */ if (!gjs_create_root_importer(js_context->context, js_context->search_path ? (const char**) js_context->search_path : NULL, true)) g_error("Failed to create root importer"); /* Now copy the global root importer (which we just created, * if it didn't exist) to our global object */ if (!gjs_define_root_importer(js_context->context, global)) g_error("Failed to point 'imports' property at root importer"); /* FIXME: We should define the Promise object before any imports, in case * the imports want to use it. Currently that's not possible as it needs to * import GLib */ if(!gjs_define_promise_object(js_context->context, global)) g_error("Failed to define global Promise object"); JS_EndRequest(js_context->context); g_mutex_lock (&contexts_lock); all_contexts = g_list_prepend(all_contexts, object); g_mutex_unlock (&contexts_lock); } 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; 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; gjs_gc_if_needed(js_context->context); return G_SOURCE_REMOVE; } void _gjs_context_schedule_gc_if_needed (GjsContext *js_context) { 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_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 == JS_GetCurrentThread(); } /** * 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->runtime); } /** * 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; JSAutoCompartment ac(js_context->context, js_context->global); JSAutoRequest ar(js_context->context); g_object_ref(G_OBJECT(js_context)); JS::RootedValue retval(js_context->context); if (!gjs_eval_with_scope(js_context->context, JS::NullPtr(), script, script_len, filename, &retval)) { 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 */ } gjs_log_exception(js_context->context); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "JS_EvaluateScript() failed"); /* 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 = NULL; gsize script_len; bool ret = true; GFile *file = g_file_new_for_commandline_arg(filename); if (!g_file_query_exists(file, NULL)) { ret = false; goto out; } if (!g_file_load_contents(file, NULL, &script, &script_len, NULL, error)) { ret = false; goto out; } if (!gjs_context_eval(js_context, script, script_len, filename, exit_status_p, error)) { ret = false; goto out; } out: g_free(script); g_object_unref(file); return ret; } 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; } cjs-3.6.1/cjs/context.h000066400000000000000000000101641320401450000147030ustar00rootroot00000000000000/* -*- 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 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 void gjs_dumpstack (void); G_END_DECLS #endif /* __GJS_CONTEXT_H__ */ cjs-3.6.1/cjs/coverage-internal.h000066400000000000000000000045251320401450000166300ustar00rootroot00000000000000/* * Copyright © 2015 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #ifndef _GJS_COVERAGE_INTERNAL_H #define _GJS_COVERAGE_INTERNAL_H #include #include "jsapi-util.h" #include "coverage.h" G_BEGIN_DECLS GjsCoverage *gjs_coverage_new_internal_with_cache(const char * const *coverage_prefixes, GjsContext *context, GFile *output_dir, GFile *cache_path); GjsCoverage *gjs_coverage_new_internal_without_cache(const char * const *prefixes, GjsContext *cx, GFile *output_dir); GBytes * gjs_serialize_statistics(GjsCoverage *coverage); JSString * gjs_deserialize_cache_to_object(GjsCoverage *coverage, GBytes *cache_bytes); bool gjs_run_script_in_coverage_compartment(GjsCoverage *coverage, const char *script); bool gjs_inject_value_into_coverage_compartment(GjsCoverage *coverage, JS::HandleValue value, const char *property); bool gjs_get_file_mtime(GFile *file, GTimeVal *mtime); char *gjs_get_file_checksum(GFile *file); bool gjs_write_cache_file(GFile *file, GBytes *cache_bytes); extern const char *GJS_COVERAGE_CACHE_FILE_NAME; G_END_DECLS #endif cjs-3.6.1/cjs/coverage.cpp000066400000000000000000001774741320401450000153670ustar00rootroot00000000000000/* * Copyright © 2014 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #include #include #include #include "coverage.h" #include "coverage-internal.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 coverage_statistics; GFile *output_dir; GFile *cache; /* tells whether priv->cache == NULL means no cache, or not specified */ bool cache_specified; } GjsCoveragePrivate; G_DEFINE_TYPE_WITH_PRIVATE(GjsCoverage, gjs_coverage, G_TYPE_OBJECT) enum { PROP_0, PROP_PREFIXES, PROP_CONTEXT, PROP_CACHE, PROP_OUTPUT_DIRECTORY, PROP_N }; static GParamSpec *properties[PROP_N] = { NULL, }; typedef struct _GjsCoverageBranchExit { unsigned int line; unsigned int hit_count; } GjsCoverageBranchExit; typedef struct _GjsCoverageBranch { GArray *exits; unsigned int point; bool hit; } GjsCoverageBranch; typedef struct _GjsCoverageFunction { char *key; unsigned int line_number; unsigned int hit_count; } GjsCoverageFunction; 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 void write_source_file_header(GOutputStream *stream, GFile *source_file) { char *path = get_file_identifier(source_file); g_output_stream_printf(stream, NULL, NULL, NULL, "SF:%s\n", path); g_free(path); } typedef struct _FunctionHitCountData { GOutputStream *stream; unsigned int *n_functions_found; unsigned int *n_functions_hit; } FunctionHitCountData; static void write_function_hit_count(GOutputStream *stream, const char *function_name, unsigned int hit_count, unsigned int *n_functions_found, unsigned int *n_functions_hit) { (*n_functions_found)++; if (hit_count > 0) (*n_functions_hit)++; g_output_stream_printf(stream, NULL, NULL, NULL, "FNDA:%d,%s\n", hit_count, function_name); } static void write_functions_hit_counts(GOutputStream *stream, GArray *functions, unsigned int *n_functions_found, unsigned int *n_functions_hit) { unsigned int i = 0; for (; i < functions->len; ++i) { GjsCoverageFunction *function = &(g_array_index(functions, GjsCoverageFunction, i)); write_function_hit_count(stream, function->key, function->hit_count, n_functions_found, n_functions_hit); } } static void write_function_foreach_func(gpointer value, gpointer user_data) { GOutputStream *stream = (GOutputStream *) user_data; GjsCoverageFunction *function = (GjsCoverageFunction *) value; g_output_stream_printf(stream, NULL, NULL, NULL, "FN:%d,%s\n", function->line_number, function->key); } static void for_each_element_in_array(GArray *array, GFunc func, gpointer user_data) { const gsize element_size = g_array_get_element_size(array); unsigned int i; char *current_array_pointer = (char *) array->data; for (i = 0; i < array->len; ++i, current_array_pointer += element_size) (*func)(current_array_pointer, user_data); } static void write_functions(GOutputStream *data_stream, GArray *functions) { for_each_element_in_array(functions, write_function_foreach_func, data_stream); } static void write_function_coverage(GOutputStream *data_stream, unsigned int n_found_functions, unsigned int n_hit_functions) { g_output_stream_printf(data_stream, NULL, NULL, NULL, "FNF:%d\n", n_found_functions); g_output_stream_printf(data_stream, NULL, NULL, NULL, "FNH:%d\n", n_hit_functions); } typedef struct _WriteAlternativeData { unsigned int *n_branch_alternatives_found; unsigned int *n_branch_alternatives_hit; GOutputStream *output_stream; gpointer *all_alternatives; bool branch_point_was_hit; } WriteAlternativeData; typedef struct _WriteBranchInfoData { unsigned int *n_branch_exits_found; unsigned int *n_branch_exits_hit; GOutputStream *output_stream; } WriteBranchInfoData; static void write_individual_branch(gpointer branch_ptr, gpointer user_data) { GjsCoverageBranch *branch = (GjsCoverageBranch *) branch_ptr; WriteBranchInfoData *data = (WriteBranchInfoData *) user_data; /* This line is not a branch, don't write anything */ if (!branch->point) return; unsigned int i = 0; for (; i < branch->exits->len; ++i) { GjsCoverageBranchExit *exit = &(g_array_index(branch->exits, GjsCoverageBranchExit, i)); unsigned int alternative_counter = exit->hit_count; unsigned int branch_point = branch->point; char *hit_count_string = NULL; if (!branch->hit) hit_count_string = g_strdup_printf("-"); else hit_count_string = g_strdup_printf("%d", alternative_counter); g_output_stream_printf(data->output_stream, NULL, NULL, NULL, "BRDA:%d,0,%d,%s\n", branch_point, i, hit_count_string); g_free(hit_count_string); ++(*data->n_branch_exits_found); if (alternative_counter > 0) ++(*data->n_branch_exits_hit); } } static void write_branch_coverage(GOutputStream *stream, GArray *branches, unsigned int *n_branch_exits_found, unsigned int *n_branch_exits_hit) { /* Write individual branches and pass-out the totals */ WriteBranchInfoData data = { n_branch_exits_found, n_branch_exits_hit, stream }; for_each_element_in_array(branches, write_individual_branch, &data); } static void write_branch_totals(GOutputStream *stream, unsigned int n_branch_exits_found, unsigned int n_branch_exits_hit) { g_output_stream_printf(stream, NULL, NULL, NULL, "BRF:%d\n", n_branch_exits_found); g_output_stream_printf(stream, NULL, NULL, NULL, "BRH:%d\n", n_branch_exits_hit); } static void write_line_coverage(GOutputStream *stream, GArray *stats, unsigned int *lines_hit_count, unsigned int *executable_lines_count) { unsigned int i = 0; for (i = 0; i < stats->len; ++i) { int hit_count_for_line = g_array_index(stats, int, i); if (hit_count_for_line == -1) continue; g_output_stream_printf(stream, NULL, NULL, NULL, "DA:%d,%d\n", i, hit_count_for_line); if (hit_count_for_line > 0) ++(*lines_hit_count); ++(*executable_lines_count); } } static void write_line_totals(GOutputStream *stream, unsigned int lines_hit_count, unsigned int executable_lines_count) { g_output_stream_printf(stream, NULL, NULL, NULL, "LH:%d\n", lines_hit_count); g_output_stream_printf(stream, NULL, NULL, NULL, "LF:%d\n", executable_lines_count); } static void write_end_of_record(GOutputStream *stream) { g_output_stream_printf(stream, NULL, NULL, NULL, "end_of_record\n"); } static void copy_source_file_to_coverage_output(GFile *source_file, GFile *destination_file) { GError *error = NULL; /* 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)) goto fail; g_clear_error(&error); } if (!g_file_copy(source_file, destination_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) { goto fail; } return; fail: char *source_uri = get_file_identifier(source_file); char *dest_uri = get_file_identifier(destination_file); g_critical("Failed to copy source file %s to destination %s: %s\n", source_uri, dest_uri, error->message); g_free(source_uri); g_free(dest_uri); g_clear_error(&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; } typedef bool (*ConvertAndInsertJSVal) (GArray *array, JSContext *context, JS::HandleValue element); static bool get_array_from_js_value(JSContext *context, JS::HandleValue value, size_t array_element_size, GDestroyNotify element_clear_func, ConvertAndInsertJSVal inserter, GArray **out_array) { g_return_val_if_fail(out_array != NULL, false); g_return_val_if_fail(*out_array == NULL, false); if (!JS_IsArrayObject(context, value)) { g_critical("Returned object from is not an array"); return false; } /* We're not preallocating any space here at the moment until * we have some profiling data that suggests a good size to * preallocate to. */ GArray *c_side_array = g_array_new(true, true, array_element_size); uint32_t js_array_len; JS::RootedObject js_array(context, &value.toObject()); if (element_clear_func) g_array_set_clear_func(c_side_array, element_clear_func); if (JS_GetArrayLength(context, js_array, &js_array_len)) { uint32_t i = 0; JS::RootedValue element(context); for (; i < js_array_len; ++i) { if (!JS_GetElement(context, js_array, i, &element)) { g_array_unref(c_side_array); gjs_throw(context, "Failed to get function names array element %d", i); return false; } if (!(inserter(c_side_array, context, element))) { g_array_unref(c_side_array); gjs_throw(context, "Failed to convert array element %d", i); return false; } } } *out_array = c_side_array; return true; } static bool convert_and_insert_unsigned_int(GArray *array, JSContext *context, JS::HandleValue element) { if (!element.isInt32() && !element.isUndefined() && !element.isNull()) { g_critical("Array element is not an integer or undefined or null"); return false; } if (element.isInt32()) { unsigned int element_integer = element.toInt32(); g_array_append_val(array, element_integer); } else { int not_executable = -1; g_array_append_val(array, not_executable); } return true; } static GArray * get_executed_lines_for(JSContext *context, JS::HandleObject coverage_statistics, JS::HandleValue filename_value) { GArray *array = NULL; JS::RootedValue rval(context); JS::AutoValueArray<1> args(context); args[0].set(filename_value); if (!JS_CallFunctionName(context, coverage_statistics, "getExecutedLinesFor", args, &rval)) { gjs_log_exception(context); return NULL; } if (!get_array_from_js_value(context, rval, sizeof(unsigned int), NULL, convert_and_insert_unsigned_int, &array)) { gjs_log_exception(context); return NULL; } return array; } static void init_covered_function(GjsCoverageFunction *function, char *key, unsigned int line_number, unsigned int hit_count) { function->key = key; function->line_number = line_number; function->hit_count = hit_count; } static void clear_coverage_function(gpointer info_location) { GjsCoverageFunction *info = (GjsCoverageFunction *) info_location; g_free(info->key); } static bool get_hit_count_and_line_data(JSContext *cx, JS::HandleObject obj, const char *description, int32_t *hit_count, int32_t *line) { JS::RootedId hit_count_name(cx, gjs_intern_string_to_id(cx, "hitCount")); if (!gjs_object_require_property(cx, obj, "function element", hit_count_name, hit_count)) return false; JS::RootedId line_number_name(cx, gjs_intern_string_to_id(cx, "line")); return gjs_object_require_property(cx, obj, "function_element", line_number_name, line); } static bool convert_and_insert_function_decl(GArray *array, JSContext *context, JS::HandleValue element) { if (!element.isObject()) { gjs_throw(context, "Function element is not an object"); return false; } JS::RootedObject object(context, &element.toObject()); JS::RootedValue function_name_property_value(context); if (!gjs_object_require_property(context, object, NULL, GJS_STRING_NAME, &function_name_property_value)) return false; char *utf8_string; if (function_name_property_value.isString()) { if (!gjs_string_to_utf8(context, function_name_property_value, &utf8_string)) { gjs_throw(context, "Failed to convert function_name to string"); return false; } } else if (function_name_property_value.isNull()) { utf8_string = NULL; } else { gjs_throw(context, "Unexpected type for function_name"); return false; } int32_t hit_count; int32_t line_number; if (!get_hit_count_and_line_data(context, object, "function element", &hit_count, &line_number)) return false; GjsCoverageFunction info; init_covered_function(&info, utf8_string, line_number, hit_count); g_array_append_val(array, info); return true; } static GArray * get_functions_for(JSContext *context, JS::HandleObject coverage_statistics, JS::HandleValue filename_value) { GArray *array = NULL; JS::RootedValue rval(context); JS::AutoValueArray<1> args(context); args[0].set(filename_value); if (!JS_CallFunctionName(context, coverage_statistics, "getFunctionsFor", args, &rval)) { gjs_log_exception(context); return NULL; } if (!get_array_from_js_value(context, rval, sizeof(GjsCoverageFunction), clear_coverage_function, convert_and_insert_function_decl, &array)) { gjs_log_exception(context); return NULL; } return array; } static void init_covered_branch(GjsCoverageBranch *branch, unsigned int point, bool was_hit, GArray *exits) { branch->point = point; branch->hit = !!was_hit; branch->exits = exits; } static void clear_coverage_branch(gpointer branch_location) { GjsCoverageBranch *branch = (GjsCoverageBranch *) branch_location; g_array_unref(branch->exits); } static bool convert_and_insert_branch_exit(GArray *array, JSContext *context, JS::HandleValue element) { if (!element.isObject()) { gjs_throw(context, "Branch exit array element is not an object"); return false; } JS::RootedObject object(context, &element.toObject()); int32_t hit_count; int32_t line; if (!get_hit_count_and_line_data(context, object, "branch exit array element", &hit_count, &line)) return false; GjsCoverageBranchExit exit = { (unsigned int) line, (unsigned int) hit_count }; g_array_append_val(array, exit); return true; } static bool convert_and_insert_branch_info(GArray *array, JSContext *context, JS::HandleValue element) { if (!element.isObject() && !element.isUndefined()) { gjs_throw(context, "Branch array element is not an object or undefined"); return false; } if (element.isObject()) { JS::RootedObject object(context, &element.toObject()); int32_t branch_point; JS::RootedId point_name(context, gjs_intern_string_to_id(context, "point")); if (!gjs_object_require_property(context, object, "branch array element", point_name, &branch_point)) return false; bool was_hit; JS::RootedId hit_name(context, gjs_intern_string_to_id(context, "hit")); if (!gjs_object_require_property(context, object, "branch array element", hit_name, &was_hit)) return false; JS::RootedValue branch_exits_value(context); GArray *branch_exits_array = NULL; if (!JS_GetProperty(context, object, "exits", &branch_exits_value) || !branch_exits_value.isObject()) { gjs_throw(context, "Failed to get exits property from element"); return false; } if (!get_array_from_js_value(context, branch_exits_value, sizeof(GjsCoverageBranchExit), NULL, convert_and_insert_branch_exit, &branch_exits_array)) { /* Already logged the exception, no need to do anything here */ return false; } GjsCoverageBranch branch; init_covered_branch(&branch, branch_point, was_hit, branch_exits_array); g_array_append_val(array, branch); } return true; } static GArray * get_branches_for(JSContext *context, JS::HandleObject coverage_statistics, JS::HandleValue filename_value) { GArray *array = NULL; JS::AutoValueArray<1> args(context); args[0].set(filename_value); JS::RootedValue rval(context); if (!JS_CallFunctionName(context, coverage_statistics, "getBranchesFor", args, &rval)) { gjs_log_exception(context); return NULL; } if (!get_array_from_js_value(context, rval, sizeof(GjsCoverageBranch), clear_coverage_branch, convert_and_insert_branch_info, &array)) { gjs_log_exception(context); return NULL; } return array; } typedef struct _GjsCoverageFileStatistics { char *filename; GArray *lines; GArray *functions; GArray *branches; } GjsCoverageFileStatistics; static bool fetch_coverage_file_statistics_from_js(JSContext *context, JS::HandleObject coverage_statistics, const char *filename, GjsCoverageFileStatistics *statistics) { JSAutoCompartment compartment(context, coverage_statistics); JSAutoRequest ar(context); JSString *filename_jsstr = JS_NewStringCopyZ(context, filename); JS::RootedValue filename_jsval(context, JS::StringValue(filename_jsstr)); GArray *lines = get_executed_lines_for(context, coverage_statistics, filename_jsval); GArray *functions = get_functions_for(context, coverage_statistics, filename_jsval); GArray *branches = get_branches_for(context, coverage_statistics, filename_jsval); if (!lines || !functions || !branches) { g_clear_pointer(&lines, g_array_unref); g_clear_pointer(&functions, g_array_unref); g_clear_pointer(&branches, g_array_unref); return false; } statistics->filename = g_strdup(filename); statistics->lines = lines; statistics->functions = functions; statistics->branches = branches; return true; } static void gjs_coverage_statistics_file_statistics_clear(gpointer data) { GjsCoverageFileStatistics *statistics = (GjsCoverageFileStatistics *) data; g_free(statistics->filename); g_array_unref(statistics->lines); g_array_unref(statistics->functions); g_array_unref(statistics->branches); } static void print_statistics_for_file(GjsCoverageFileStatistics *file_statistics, GFile *output_dir, GOutputStream *ostream) { /* The source file could be a resource, so we must use * g_file_new_for_commandline_arg() to disambiguate between URIs and * filesystem paths. */ GFile *source = g_file_new_for_commandline_arg(file_statistics->filename); char *diverged_paths = find_diverging_child_components(source, output_dir); GFile *dest = g_file_resolve_relative_path(output_dir, diverged_paths); copy_source_file_to_coverage_output(source, dest); g_object_unref(source); write_source_file_header(ostream, dest); g_object_unref(dest); write_functions(ostream, file_statistics->functions); unsigned int functions_hit_count = 0; unsigned int functions_found_count = 0; write_functions_hit_counts(ostream, file_statistics->functions, &functions_found_count, &functions_hit_count); write_function_coverage(ostream, functions_found_count, functions_hit_count); unsigned int branches_hit_count = 0; unsigned int branches_found_count = 0; write_branch_coverage(ostream, file_statistics->branches, &branches_found_count, &branches_hit_count); write_branch_totals(ostream, branches_found_count, branches_hit_count); unsigned int lines_hit_count = 0; unsigned int executable_lines_count = 0; write_line_coverage(ostream, file_statistics->lines, &lines_hit_count, &executable_lines_count); write_line_totals(ostream, lines_hit_count, executable_lines_count); write_end_of_record(ostream); g_free(diverged_paths); } static char ** get_covered_files(GjsCoverage *coverage) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(context); JSAutoCompartment ac(context, priv->coverage_statistics); JS::RootedObject rooted_priv(context, priv->coverage_statistics); JS::RootedValue rval(context); char **files = NULL; uint32_t n_files; if (!JS_CallFunctionName(context, rooted_priv, "getCoveredFiles", JS::HandleValueArray::empty(), &rval)) { gjs_log_exception(context); return NULL; } if (!rval.isObject()) return NULL; JS::RootedObject files_obj(context, &rval.toObject()); if (!JS_GetArrayLength(context, files_obj, &n_files)) return NULL; files = g_new0(char *, n_files + 1); JS::RootedValue element(context); for (uint32_t i = 0; i < n_files; i++) { char *file; if (!JS_GetElement(context, files_obj, i, &element)) goto error; if (!gjs_string_to_utf8(context, element, &file)) goto error; files[i] = file; } files[n_files] = NULL; return files; error: g_strfreev(files); return NULL; } bool gjs_get_file_mtime(GFile *file, GTimeVal *mtime) { GError *error = NULL; GFileInfo *info = g_file_query_info(file, "time::modified,time::modified-usec", G_FILE_QUERY_INFO_NONE, NULL, &error); if (!info) { char *path = get_file_identifier(file); g_warning("Failed to get modification time of %s, " "falling back to checksum method for caching. Reason was: %s", path, error->message); g_clear_object(&info); return false; } g_file_info_get_modification_time(info, mtime); g_clear_object(&info); /* For some URI types, eg, resources, the operation getting * the mtime might succeed, but by default zero is returned. * * Check if that is the case for both tv_sec and tv_usec and if * so return false. */ return !(mtime->tv_sec == 0 && mtime->tv_usec == 0); } static GBytes * read_all_bytes_from_file(GFile *file) { /* We have to use g_file_query_exists here since * g_file_test(path, G_FILE_TEST_EXISTS) is implemented in terms * of access(), which doesn't work with resource paths. */ if (!g_file_query_exists(file, NULL)) return NULL; gsize len = 0; gchar *data = NULL; GError *error = NULL; if (!g_file_load_contents(file, NULL, &data, &len, NULL, &error)) { char *path = get_file_identifier(file); g_critical("Unable to read bytes from: %s, reason was: %s\n", path, error->message); g_clear_error(&error); g_free(path); return NULL; } return g_bytes_new_take(data, len); } gchar * gjs_get_file_checksum(GFile *file) { GBytes *data = read_all_bytes_from_file(file); if (!data) return NULL; gchar *checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA512, data); g_bytes_unref(data); return checksum; } /* The binary data for the cache has the following structure: * * { * array [ tuple { * string filename; * string? checksum; * tuple? { * mtime_sec; * mtime_usec; * } * array [ * int line; * ] executable lines; * array [ tuple { * int branch_point; * array [ * int line; * ] exits; * } branch_info ] branches; * array [ tuple { * int line; * string key; * } function ] functions; * } file ] files; */ const char *COVERAGE_STATISTICS_CACHE_BINARY_DATA_TYPE = "a(sm(xx)msaia(iai)a(is))"; GBytes * gjs_serialize_statistics(GjsCoverage *coverage) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *js_context = (JSContext *) gjs_context_get_native_context(priv->context); JSRuntime *js_runtime = JS_GetRuntime(js_context); JSAutoRequest ar(js_context); JSAutoCompartment ac(js_context, priv->coverage_statistics); JS::RootedObject rooted_priv(js_context, priv->coverage_statistics); JS::RootedValue string_value_return(js_runtime); if (!JS_CallFunctionName(js_context, rooted_priv, "stringify", JS::HandleValueArray::empty(), &string_value_return)) { gjs_log_exception(js_context); return NULL; } if (!string_value_return.isString()) return NULL; /* Free'd by g_bytes_new_take */ char *statistics_as_json_string = NULL; if (!gjs_string_to_utf8(js_context, string_value_return.get(), &statistics_as_json_string)) { gjs_log_exception(js_context); return NULL; } return g_bytes_new_take((guint8 *) statistics_as_json_string, strlen(statistics_as_json_string)); } static JSString * gjs_deserialize_cache_to_object_for_compartment(JSContext *context, JS::HandleObject global_object, GBytes *cache_data) { JSAutoRequest ar(context); JSAutoCompartment ac(context, JS_GetGlobalForObject(context, global_object)); gsize len = 0; auto string = static_cast(g_bytes_get_data(cache_data, &len)); return JS_NewStringCopyN(context, string, len); } JSString * gjs_deserialize_cache_to_object(GjsCoverage *coverage, GBytes *cache_data) { /* Deserialize into an object with the following structure: * * object = { * 'filename': { * contents: (file contents), * nLines: (number of lines in file), * lines: Number[nLines + 1], * branches: Array for n_branches of { * point: branch_point, * exits: Number[nLines + 1] * }, * functions: Array for n_functions of { * key: function_name,r * line: line * } * } */ GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(context); JSAutoCompartment ac(context, priv->coverage_statistics); JS::RootedObject global_object(JS_GetRuntime(context), JS_GetGlobalForObject(context, priv->coverage_statistics)); return gjs_deserialize_cache_to_object_for_compartment(context, global_object, cache_data); } static GArray * gjs_fetch_statistics_from_js(GjsCoverage *coverage, gchar **coverage_files) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *js_context = (JSContext *) gjs_context_get_native_context(priv->context); GArray *file_statistics_array = g_array_new(false, false, sizeof(GjsCoverageFileStatistics)); g_array_set_clear_func(file_statistics_array, gjs_coverage_statistics_file_statistics_clear); JS::RootedObject rooted_coverage_statistics(JS_GetRuntime(js_context), priv->coverage_statistics); char **file_iter = coverage_files; while (*file_iter) { GjsCoverageFileStatistics statistics; if (fetch_coverage_file_statistics_from_js(js_context, rooted_coverage_statistics, *file_iter, &statistics)) g_array_append_val(file_statistics_array, statistics); else g_warning("Couldn't fetch statistics for %s", *file_iter); ++file_iter; } return file_statistics_array; } bool gjs_write_cache_file(GFile *file, GBytes *cache) { gsize cache_len = 0; char *cache_data = (char *) g_bytes_get_data(cache, &cache_len); GError *error = NULL; if (!g_file_replace_contents(file, cache_data, cache_len, NULL, false, G_FILE_CREATE_NONE, NULL, NULL, &error)) { char *path = get_file_identifier(file); g_warning("Failed to write all bytes to %s, reason was: %s\n", path, error->message); g_warning("Will remove this file to prevent inconsistent cache " "reads next time."); g_clear_error(&error); if (!g_file_delete(file, NULL, &error)) { g_assert(error != NULL); g_critical("Deleting %s failed because %s! You will need to " "delete it manually before running the coverage " "mode again.", path, error->message); g_clear_error(&error); } g_free(path); return false; } return true; } static bool coverage_statistics_has_stale_cache(GjsCoverage *coverage) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *js_context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(js_context); JSAutoCompartment ac(js_context, priv->coverage_statistics); JS::RootedObject rooted_priv(js_context, priv->coverage_statistics); JS::RootedValue stale_cache_value(js_context); if (!JS_CallFunctionName(js_context, rooted_priv, "staleCache", JS::HandleValueArray::empty(), &stale_cache_value)) { gjs_log_exception(js_context); g_error("Failed to call into javascript to get stale cache value. This is a bug"); } return stale_cache_value.toBoolean(); } static unsigned int _suppressed_coverage_messages_count = 0; /** * gjs_coverage_write_statistics: * @coverage: A #GjsCoverage * @output_directory: A directory to write coverage information to. Scripts * which were provided as part of the coverage-paths 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, even if they * were never actually executed. */ void gjs_coverage_write_statistics(GjsCoverage *coverage) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); GError *error = NULL; JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoCompartment compartment(context, priv->coverage_statistics); JSAutoRequest ar(context); /* Create output directory if it doesn't exist */ if (!g_file_make_directory_with_parents(priv->output_dir, NULL, &error)) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_critical("Could not create coverage output: %s", error->message); g_clear_error(&error); return; } g_clear_error(&error); } GFile *output_file = g_file_get_child(priv->output_dir, "coverage.lcov"); GOutputStream *ostream = G_OUTPUT_STREAM(g_file_append_to(output_file, G_FILE_CREATE_NONE, NULL, &error)); char **executed_coverage_files = get_covered_files(coverage); GArray *file_statistics_array = gjs_fetch_statistics_from_js(coverage, executed_coverage_files); for (size_t i = 0; i < file_statistics_array->len; ++i) { GjsCoverageFileStatistics *statistics = &(g_array_index(file_statistics_array, GjsCoverageFileStatistics, i)); /* Only print statistics if the file was actually executed */ for (char **iter = executed_coverage_files; *iter; ++iter) { if (g_strcmp0(*iter, statistics->filename) == 0) { print_statistics_for_file(statistics, priv->output_dir, ostream); /* Inner loop */ break; } } } g_strfreev(executed_coverage_files); const bool has_cache_path = priv->cache != NULL; const bool cache_is_stale = coverage_statistics_has_stale_cache(coverage); if (has_cache_path && cache_is_stale) { GBytes *cache_data = gjs_serialize_statistics(coverage); gjs_write_cache_file(priv->cache, cache_data); g_bytes_unref(cache_data); } char *output_file_path = g_file_get_path(priv->output_dir); g_message("Wrote coverage statistics to %s", output_file_path); if (_suppressed_coverage_messages_count) { g_message("There were %i suppressed message(s) when collecting " "coverage, set GJS_SHOW_COVERAGE_MESSAGES to see them.", _suppressed_coverage_messages_count); _suppressed_coverage_messages_count = 0; } g_free(output_file_path); g_array_unref(file_statistics_array); g_object_unref(ostream); g_object_unref(output_file); } static void gjs_coverage_init(GjsCoverage *self) { } static JSClass coverage_global_class = { "GjsCoverageGlobal", JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST) | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ NULL, /* convert */ NULL, /* finalize */ NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ JS_GlobalObjectTraceHook }; static bool gjs_context_eval_file_in_compartment(GjsContext *context, const char *filename, JS::HandleObject compartment_object, GError **error) { char *script = NULL; gsize script_len = 0; GFile *file = g_file_new_for_commandline_arg(filename); if (!g_file_load_contents(file, NULL, &script, &script_len, NULL, error)) { g_object_unref(file); return false; } g_object_unref(file); int start_line_number = 1; const char *stripped_script = gjs_strip_unix_shebang(script, &script_len, &start_line_number); JSContext *js_context = (JSContext *) gjs_context_get_native_context(context); JSAutoCompartment compartment(js_context, compartment_object); JS::CompileOptions options(js_context); options.setUTF8(true) .setFileAndLine(filename, start_line_number) .setSourceIsLazy(true); JS::RootedScript compiled_script(js_context); if (!JS::Compile(js_context, compartment_object, options, stripped_script, script_len, &compiled_script)) return false; if (!JS::CloneAndExecuteScript(js_context, compartment_object, compiled_script)) { g_free(script); gjs_log_exception(js_context); g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED, "Failed to evaluate %s", filename); return false; } g_free(script); return true; } static bool coverage_log(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); char *s; JSExceptionState *exc_state; if (argc != 1) { gjs_throw(context, "Must pass a single argument to log()"); return false; } JSAutoRequest ar(context); if (!g_getenv("GJS_SHOW_COVERAGE_MESSAGES")) { _suppressed_coverage_messages_count++; argv.rval().setUndefined(); return true; } /* JS::ToString might throw, in which case we will only log that the value * could not be converted to string */ exc_state = JS_SaveExceptionState(context); JS::RootedString jstr(context, JS::ToString(context, argv[0])); if (jstr != NULL) argv[0].setString(jstr); // GC root JS_RestoreExceptionState(context, exc_state); if (jstr == NULL) { g_message("JS LOG: "); return true; } if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) { return false; } g_message("JS COVERAGE MESSAGE: %s", s); g_free(s); argv.rval().setUndefined(); return true; } static GFile * get_file_from_call_args_filename(JSContext *context, JS::CallArgs &args) { char *filename = NULL; if (!gjs_parse_call_args(context, "getFileContents", args, "s", "filename", &filename)) return NULL; /* path could be a resource, so use g_file_new_for_commandline_arg. */ GFile *file = g_file_new_for_commandline_arg(filename); g_free(filename); return file; } static bool coverage_get_file_modification_time(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); GTimeVal mtime; bool ret = false; GFile *file = get_file_from_call_args_filename(context, args); if (!file) return false; if (gjs_get_file_mtime(file, &mtime)) { JS::AutoValueArray<2> mtime_values_array(context); mtime_values_array[0].setInt32(mtime.tv_sec); mtime_values_array[1].setInt32(mtime.tv_usec); JS::RootedObject array_obj(context, JS_NewArrayObject(context, mtime_values_array)); if (array_obj == NULL) goto out; args.rval().setObject(*array_obj); } else { args.rval().setNull(); } ret = true; out: g_object_unref(file); return ret; } static bool coverage_get_file_checksum(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JSRuntime *runtime = JS_GetRuntime(context); GFile *file = get_file_from_call_args_filename(context, args); if (!file) return false; char *checksum = gjs_get_file_checksum(file); if (!checksum) { char *filename = get_file_identifier(file); gjs_throw(context, "Failed to read %s and get its checksum", filename); g_free(filename); g_object_unref(file); return false; } JS::RootedString rooted_checksum(runtime, JS_NewStringCopyZ(context, checksum)); args.rval().setString(rooted_checksum); g_object_unref(file); g_free(checksum); return true; } static bool coverage_get_file_contents(JSContext *context, unsigned argc, JS::Value *vp) { bool ret = false; JS::CallArgs args = JS::CallArgsFromVp(argc, vp); GFile *file = NULL; char *script = NULL; gsize script_len; GError *error = NULL; file = get_file_from_call_args_filename(context, args); if (!file) return false; if (!g_file_load_contents(file, NULL, &script, &script_len, NULL, &error)) { char *filename = get_file_identifier(file); gjs_throw(context, "Failed to load contents for filename %s: %s", filename, error->message); g_free(filename); goto out; } args.rval().setString(JS_NewStringCopyN(context, script, script_len)); ret = true; out: g_clear_error(&error); g_object_unref(file); g_free(script); return ret; } static JSFunctionSpec coverage_funcs[] = { JS_FS("log", coverage_log, 1, GJS_MODULE_PROP_FLAGS), JS_FS("getFileContents", coverage_get_file_contents, 1, GJS_MODULE_PROP_FLAGS), JS_FS("getFileModificationTime", coverage_get_file_modification_time, 1, GJS_MODULE_PROP_FLAGS), JS_FS("getFileChecksum", coverage_get_file_checksum, 1, GJS_MODULE_PROP_FLAGS), JS_FS_END }; static void coverage_statistics_tracer(JSTracer *trc, void *data) { GjsCoverage *coverage = (GjsCoverage *) data; GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JS_CallObjectTracer(trc, &priv->coverage_statistics, "coverage_statistics"); } /* This function is mainly used in the tests in order to fiddle with * the internals of the coverage statisics collector on the coverage * compartment side */ bool gjs_run_script_in_coverage_compartment(GjsCoverage *coverage, const char *script) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *js_context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoCompartment ac(js_context, priv->coverage_statistics); JSAutoRequest ar(js_context); JS::CompileOptions options(js_context); options.setUTF8(true); JS::RootedValue rval(js_context); JS::RootedObject global(js_context, JS_GetGlobalForObject(js_context, priv->coverage_statistics)); if (!JS::Evaluate(js_context, global, options, script, strlen(script), &rval)) { gjs_log_exception(js_context); g_warning("Failed to evaluate "); return false; } return true; } bool gjs_inject_value_into_coverage_compartment(GjsCoverage *coverage, JS::HandleValue value, const char *property) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); JSContext *js_context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(js_context); JSAutoCompartment ac(js_context, priv->coverage_statistics); JS::RootedObject coverage_global_scope(JS_GetRuntime(js_context), JS_GetGlobalForObject(js_context, priv->coverage_statistics)); if (!JS_SetProperty(js_context, coverage_global_scope, property, value)) { g_warning("Failed to set property %s to requested value", property); return false; } return true; } /* Gets the root import and wraps it into a cross-compartment * object so that it can be used in the debugger compartment */ static JSObject * gjs_wrap_root_importer_in_compartment(JSContext *context, JS::HandleObject compartment) { JSAutoRequest ar(context); JSAutoCompartment ac(context, compartment); JS::RootedValue importer (JS_GetRuntime(context), gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS)); g_assert (!importer.isUndefined()); JS::RootedObject wrapped_importer(JS_GetRuntime(context), importer.toObjectOrNull()); if (!JS_WrapObject(context, &wrapped_importer)) { return NULL; } return wrapped_importer; } static bool bootstrap_coverage(GjsCoverage *coverage) { static const char *coverage_script = "resource:///org/cinnamon/cjs/modules/coverage.js"; GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); GBytes *cache_bytes = NULL; GError *error = NULL; JSContext *context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(context); JSObject *debuggee = gjs_get_import_global(context); JS::CompartmentOptions options; options.setVersion(JSVERSION_LATEST); JS::RootedObject debugger_compartment(JS_GetRuntime(context), JS_NewGlobalObject(context, &coverage_global_class, NULL, JS::FireOnNewGlobalHook, options)); { JSAutoCompartment compartment(context, debugger_compartment); JS::RootedObject debuggeeWrapper(context, debuggee); if (!JS_WrapObject(context, &debuggeeWrapper)) { gjs_throw(context, "Failed to wrap debugeee"); return false; } JS::RootedValue debuggeeWrapperValue(context, JS::ObjectValue(*debuggeeWrapper)); if (!JS_SetProperty(context, debugger_compartment, "debuggee", debuggeeWrapperValue)) { gjs_throw(context, "Failed to set debuggee property"); return false; } if (!JS_InitStandardClasses(context, debugger_compartment)) { gjs_throw(context, "Failed to init standard classes"); return false; } if (!JS_InitReflect(context, debugger_compartment)) { gjs_throw(context, "Failed to init Reflect"); return false; } if (!JS_DefineDebuggerObject(context, debugger_compartment)) { gjs_throw(context, "Failed to init Debugger"); return false; } JS::RootedObject wrapped_importer(JS_GetRuntime(context), gjs_wrap_root_importer_in_compartment(context, debugger_compartment));; if (!wrapped_importer) { gjs_throw(context, "Failed to wrap root importer in debugger compartment"); return false; } /* Now copy the global root importer (which we just created, * if it didn't exist) to our global object */ if (!gjs_define_root_importer_object(context, debugger_compartment, wrapped_importer)) { gjs_throw(context, "Failed to set 'imports' on debugger compartment"); return false; } if (!JS_DefineFunctions(context, debugger_compartment, &coverage_funcs[0])) g_error("Failed to init coverage"); if (!gjs_context_eval_file_in_compartment(priv->context, coverage_script, debugger_compartment, &error)) g_error("Failed to eval coverage script: %s\n", error->message); JS::RootedObject coverage_statistics_constructor(context); JS::RootedId coverage_statistics_name(context, gjs_intern_string_to_id(context, "CoverageStatistics")); if (!gjs_object_require_property(context, debugger_compartment, "debugger compartment", coverage_statistics_name, &coverage_statistics_constructor)) return false; /* Create value for holding the cache. This will be undefined if * the cache does not exist, otherwise it will be an object set * to the value of the cache */ JS::RootedValue cache_value(context); if (priv->cache) cache_bytes = read_all_bytes_from_file(priv->cache); if (cache_bytes) { JSString *cache_object = gjs_deserialize_cache_to_object_for_compartment(context, debugger_compartment, cache_bytes); cache_value.setString(cache_object); g_bytes_unref(cache_bytes); } /* Now create the array to pass the desired prefixes over */ JSObject *prefixes = gjs_build_string_array(context, -1, priv->prefixes); JS::AutoValueArray<3> coverage_statistics_constructor_args(context); coverage_statistics_constructor_args[0].setObject(*prefixes); coverage_statistics_constructor_args[1].set(cache_value); coverage_statistics_constructor_args[2] .setBoolean(g_getenv("GJS_DEBUG_COVERAGE_EXECUTED_LINES")); JSObject *coverage_statistics = JS_New(context, coverage_statistics_constructor, coverage_statistics_constructor_args); if (!coverage_statistics) { gjs_throw(context, "Failed to create coverage_statitiscs object"); return false; } /* Add a tracer, as suggested by jdm on #jsapi */ JS_AddExtraGCRootsTracer(JS_GetRuntime(context), coverage_statistics_tracer, coverage); priv->coverage_statistics = coverage_statistics; } 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->coverage_statistics) JS::Heap(); if (!priv->cache_specified) { g_message("Cache path was not given, picking default one"); priv->cache = g_file_new_for_path(".internal-gjs-coverage-cache"); } /* We now enable Ion and BaselineJIT in coverage mode. See the comment * in gjs/runtime.cpp:gjs_clear_thread_runtime for some important * information regarding runtime lifecycle management and garbage collection * bugs in js24 */ 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: priv->cache_specified = true; /* g_value_dup_object() adds a reference if not NULL */ priv->cache = static_cast(g_value_dup_object(value)); 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_clear_js_side_statistics_from_coverage_object(GjsCoverage *coverage) { GjsCoveragePrivate *priv = (GjsCoveragePrivate *) gjs_coverage_get_instance_private(coverage); if (priv->coverage_statistics) { /* Remove tracer before disposing the context */ JSContext *js_context = (JSContext *) gjs_context_get_native_context(priv->context); JSAutoRequest ar(js_context); JSAutoCompartment ac(js_context, priv->coverage_statistics); JS::RootedObject rooted_priv(js_context, priv->coverage_statistics); JS::RootedValue rval(JS_GetRuntime(js_context)); if (!JS_CallFunctionName(js_context, rooted_priv, "deactivate", JS::HandleValueArray::empty(), &rval)) { gjs_log_exception(js_context); g_error("Failed to deactivate debugger - this is a fatal error"); } /* Remove GC roots trace after we've decomissioned the object * and no longer need it to be traced here. */ JS_RemoveExtraGCRootsTracer(JS_GetRuntime(js_context), coverage_statistics_tracer, coverage); priv->coverage_statistics = NULL; } } 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 */ gjs_clear_js_side_statistics_from_coverage_object(coverage); 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); g_clear_object(&priv->cache); priv->coverage_statistics.~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", "Cache", "File containing a cache to preload ASTs from", G_TYPE_FILE, (GParamFlags) (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); 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, using a cache in a temporary file to * pre-fill the AST information for the specified scripts in @prefixes, so long * as the data in the cache has the same mtime as those scripts. * * 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; } GjsCoverage * gjs_coverage_new_internal_with_cache(const char * const *coverage_prefixes, GjsContext *context, GFile *output_dir, GFile *cache) { GjsCoverage *coverage = GJS_COVERAGE(g_object_new(GJS_TYPE_COVERAGE, "prefixes", coverage_prefixes, "context", context, "cache", cache, "output-directory", output_dir, NULL)); return coverage; } GjsCoverage * gjs_coverage_new_internal_without_cache(const char * const *prefixes, GjsContext *cx, GFile *output_dir) { return gjs_coverage_new_internal_with_cache(prefixes, cx, output_dir, NULL); } cjs-3.6.1/cjs/coverage.h000066400000000000000000000040021320401450000150040ustar00rootroot00000000000000/* * Copyright © 2014 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 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-3.6.1/cjs/gjs.h000066400000000000000000000025111320401450000137770ustar00rootroot00000000000000/* -*- 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 #endif /* __GJS_GJS_H__ */ cjs-3.6.1/cjs/importer.cpp000066400000000000000000001113051320401450000154120ustar00rootroot00000000000000/* -*- 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 "native.h" #include #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 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; char *path = NULL; GjsAutoChar output; if (module_path.isNull()) { output = g_strdup_printf("[%s root]", klass->name); } else { if (!gjs_string_to_utf8(cx, module_path, &path)) return false; output = g_strdup_printf("[%s %s]", klass->name, path); } args.rval().setString(JS_NewStringCopyZ(cx, output)); g_free(path); 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; /* 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, /* don't set ENUMERATE since we wouldn't want to copy * this symbol to any other object for example. */ JSPROP_READONLY | JSPROP_PERMANENT)) return false; } /* Null is used instead of undefined to make sure we don't try to invoke * a "resolve" operation. */ JS::RootedValue module_name_val(context, JS::NullValue()); JS::RootedValue parent_module_val(context, JS::NullValue()); JS::RootedValue module_path(context, JS::NullValue()); 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 { char *parent_path = NULL; if (!gjs_string_to_utf8(context, parent_module_path, &parent_path)) return false; module_path_buf = g_strdup_printf("%s.%s", parent_path, module_name); g_free(parent_path); } module_path.setString(JS_NewStringCopyZ(context, module_path_buf)); } /* don't set ENUMERATE since we wouldn't want to copy these symbols to any * other object for example. */ if (!JS_DefineProperty(context, module_obj, "__moduleName__", module_name_val, JSPROP_READONLY | JSPROP_PERMANENT)) return false; if (!JS_DefineProperty(context, module_obj, "__parentModule__", parent_module_val, JSPROP_READONLY | JSPROP_PERMANENT)) return false; if (!JS_DefineProperty(context, module_obj, "__modulePath__", module_path, JSPROP_READONLY | JSPROP_PERMANENT)) return false; return true; } static bool import_directory(JSContext *context, JS::HandleObject obj, const char *name, const char **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 != NULL; } static bool define_import(JSContext *context, JS::HandleObject obj, JS::HandleObject module_obj, const char *name) { if (!JS_DefineProperty(context, obj, name, module_obj, GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to define '%s' in importer", name); return false; } return true; } /* Make the property we set in define_import permament; * we do this after the import succesfully completes. */ static bool seal_import(JSContext *cx, JS::HandleObject obj, const char *name) { JS::Rooted descr(cx); if (!JS_GetOwnPropertyDescriptor(cx, obj, name, &descr) || descr.object() == NULL) { gjs_debug(GJS_DEBUG_IMPORTER, "Failed to get attributes to seal '%s' in importer", name); return false; } /* COMPAT: in mozjs45 use .setConfigurable(false) and the form of * JS_DefineProperty that takes the JSPropertyDescriptor directly */ if (!JS_DefineProperty(cx, descr.object(), name, descr.value(), descr.attributes() | JSPROP_PERMANENT, JS_PROPERTYOP_GETTER(descr.getter()), JS_PROPERTYOP_SETTER(descr.setter()))) { 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); } } static bool module_to_string(JSContext *cx, unsigned argc, JS::Value *vp) { GJS_GET_THIS(cx, argc, vp, args, module); JS::RootedValue module_path(cx); if (!gjs_object_get_property(cx, module, GJS_STRING_MODULE_PATH, &module_path)) return false; g_assert(!module_path.isNull()); char *path = NULL; if (!gjs_string_to_utf8(cx, module_path, &path)) return false; GjsAutoChar output = g_strdup_printf("[GjsModule %s]", path); args.rval().setString(JS_NewStringCopyZ(cx, output)); g_free(path); return true; } static bool import_native_file(JSContext *context, JS::HandleObject obj, const char *name) { JS::RootedObject module_obj(context); gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", name); if (!gjs_import_native_module(context, name, &module_obj)) return false; if (!define_meta_properties(context, module_obj, NULL, name, obj)) return false; if (!JS_DefineFunction(context, module_obj, "toString", module_to_string, 0, 0)) return false; if (JS_IsExceptionPending(context)) { /* I am not sure whether this can happen, but if it does we want to trap it. */ gjs_debug(GJS_DEBUG_IMPORTER, "Module '%s' reported an exception but gjs_import_native_module() returned true", name); return false; } JS::RootedValue v_module(context, JS::ObjectValue(*module_obj)); return JS_DefineProperty(context, obj, name, v_module, GJS_MODULE_PROP_FLAGS); } static bool import_file(JSContext *context, const char *name, 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 gboolean is_extension_module (const gchar *path) { if (g_strstr_len (path, -1, "applets") || g_strstr_len (path, -1, "desklets") || g_strstr_len (path, -1, "extensions")) return TRUE; return FALSE; } 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 (!is_extension_module (full_path) && 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_file (context, "__init__", 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 == NULL) return; JS::AutoIdArray ids(cx, JS_Enumerate(cx, module_obj)); if (!ids) return; for (ix = 0, length = ids.length(); ix < length; ix++) prop_ids.append(ids[ix]); } /* 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, const char *name, GFile *file) { bool retval = false; char *full_path = NULL; JS::RootedObject module_obj(context, JS_NewPlainObject(context)); if (!define_import(context, obj, module_obj, name)) goto out; if (!import_file(context, name, file, 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 (!JS_DefineFunction(context, module_obj, "toString", module_to_string, 0, 0)) goto out; if (!seal_import(context, obj, 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, const char *name) { char *filename; char *full_path; char *dirname = NULL; JS::RootedObject search_path(context); guint32 search_path_len; guint32 i; bool result; GPtrArray *directories; GFile *gfile; bool exists; if (!gjs_object_require_property(context, obj, "importer", GJS_STRING_SEARCH_PATH, &search_path)) return false; if (!JS_IsArrayObject(context, search_path)) { 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; } result = false; filename = g_strdup_printf("%s.js", name); full_path = NULL; directories = NULL; JS::RootedValue elem(context); /* First try importing an internal module like byteArray */ if (priv->is_root && gjs_is_registered_native_module(context, obj, name) && import_native_file(context, obj, name)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); result = true; goto out; } 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 */ goto out; } if (elem.isUndefined()) continue; if (!elem.isString()) { gjs_throw(context, "importer searchPath contains non-string"); goto out; } g_free(dirname); dirname = NULL; if (!gjs_string_to_utf8(context, elem, &dirname)) goto out; /* Error message already set */ /* Ignore empty path elements */ if (dirname[0] == '\0') continue; /* Try importing __init__.js and loading the symbol from it */ import_symbol_from_init_js(context, obj, dirname, name, &result); if (result) goto out; /* Second try importing a directory (a sub-importer) */ if (full_path) g_free(full_path); full_path = g_build_filename(dirname, name, NULL); 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, name); if (directories == NULL) { directories = g_ptr_array_new(); } g_ptr_array_add(directories, full_path); /* don't free it twice - pass ownership to ptr array */ full_path = NULL; } g_object_unref(gfile); /* 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 != NULL) { continue; } /* Third, if it's not a directory, try importing a file */ g_free(full_path); full_path = g_build_filename(dirname, filename, NULL); 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); g_object_unref(gfile); continue; } if (import_file_on_module (context, obj, name, gfile)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported module '%s'", name); result = true; } g_object_unref(gfile); /* 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 */ goto out; } if (directories != NULL) { /* NULL-terminate the char** */ g_ptr_array_add(directories, NULL); if (import_directory(context, obj, name, (const char**) directories->pdata)) { gjs_debug(GJS_DEBUG_IMPORTER, "successfully imported directory '%s'", name); result = true; } } out: if (directories != NULL) { char **str_array; /* NULL-terminate the char** * (maybe for a second time, but doesn't matter) */ g_ptr_array_add(directories, NULL); str_array = (char**) directories->pdata; g_ptr_array_free(directories, false); g_strfreev(str_array); } g_free(full_path); g_free(filename); g_free(dirname); if (!result && !JS_IsExceptionPending(context)) { /* If no exception occurred, the problem is just that we got to the * end of the path. Be sure an exception is set. */ gjs_throw_custom(context, "Error", "ImportError", "No JS module '%s' found in search path", name); } return result; } /* 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) { Importer *priv; guint32 search_path_len; guint32 i; 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)) { 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); for (i = 0; i < search_path_len; ++i) { char *dirname = NULL; 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; } if (!gjs_string_to_utf8(context, elem, &dirname)) return false; /* Error message already set */ 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); g_free(dirname); GjsAutoUnref direnum = g_file_enumerate_children(dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); while (true) { GFileInfo *info; GError *error = NULL; info = g_file_enumerator_next_file(direnum, NULL, &error); if (error != NULL) { g_error_free (error); break; } if (info == NULL) break; GjsAutoChar filename = g_path_get_basename(g_file_info_get_name(info)); /* 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) { properties.append(gjs_intern_string_to_id(context, filename)); } 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); properties.append(gjs_intern_string_to_id(context, filename_noext)); } } } return true; } /* * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static bool importer_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Importer *priv; char *name = NULL; jsid module_init_name; module_init_name = gjs_context_get_const_string(context, GJS_STRING_MODULE_INIT); if (id == module_init_name) { *resolved = false; return true; } if (!gjs_get_string_id(context, id, &name)) return false; /* let Object.prototype resolve these */ if (strcmp(name, "valueOf") == 0 || strcmp(name, "toString") == 0 || strcmp(name, "__iterator__") == 0) { *resolved = false; g_free(name); return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_IMPORTER, "Resolve prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { /* we are the prototype, or have the wrong class */ *resolved = false; g_free(name); return true; } JSAutoRequest ar(context); if (!do_import(context, obj, priv, name)) { g_free(name); return false; } *resolved = true; g_free(name); return true; } GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(importer) static bool gjs_importer_add_subimporter(JSContext *context, unsigned argc, JS::Value *vp) { if (argc != 2) { gjs_throw(context, "Must pass two arguments to addSubImporter()"); return false; } GJS_GET_THIS(context, argc, vp, argv, importer); bool success = false; JSExceptionState *exc_state; char *name; char *path; char *search_path[2] = { 0, 0 }; JS_BeginRequest(context); /* JS::ToString might throw, in which case we will only log that the value * could not be converted to string */ exc_state = JS_SaveExceptionState(context); JS::RootedString name_str(context, JS::ToString(context, argv[0])); if (name_str != NULL) argv[0].setString(name_str); JS_RestoreExceptionState(context, exc_state); if (name_str == NULL) { g_message("addSubImporter: "); JS_EndRequest(context); return false; } exc_state = JS_SaveExceptionState(context); JS::RootedString path_str(context, JS::ToString(context, argv[1])); if (path_str != NULL) argv[1].setString(path_str); JS_RestoreExceptionState(context, exc_state); if (path_str == NULL) { g_message("addSubImporter: "); JS_EndRequest(context); return false; } if (!gjs_string_to_utf8(context, JS::StringValue(name_str), &name)) goto out; if (!gjs_string_to_utf8(context, JS::StringValue(path_str), &path)) goto out; search_path[0] = path; gjs_define_importer(context, importer, name, (const char **)search_path, FALSE); JS_EndRequest(context); argv.rval().setUndefined(); success = true; out: g_free (name); g_free (path); return success; } 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 == NULL) 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. */ const js::Class gjs_importer_real_class = { "GjsFileImporter", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate (see below) */ importer_resolve, NULL, /* convert */ importer_finalize, NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ NULL, /* trace */ JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, { NULL, /* lookupProperty */ NULL, /* defineProperty */ NULL, /* hasProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* getOwnPropertyDescriptor */ NULL, /* deleteProperty */ NULL, /* watch */ NULL, /* unwatch */ NULL, /* getElements */ importer_enumerate, NULL, /* thisObject */ } }; 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) JSFunctionSpec gjs_global_importer_funcs[] = { JS_FS("addSubImporter", gjs_importer_add_subimporter, 0, 0), JS_FS_END }; static JSObject* importer_new(JSContext *context, bool is_root) { Importer *priv; JS::RootedObject proto(context); if (!gjs_importer_define_proto(context, JS::NullPtr(), &proto)) g_error("Error creating importer prototype"); JS::RootedObject importer(context, JS_NewObjectWithGivenProto(context, &gjs_importer_class, proto)); if (importer == NULL) 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 **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)) 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; } JSObject* gjs_define_importer(JSContext *context, JS::HandleObject in_object, const char *importer_name, const char **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; } /* If this were called twice for the same runtime with different args it * would basically be a bug, but checking for that is a lot of code so * we just ignore all calls after the first and hope the args are the same. */ bool gjs_create_root_importer(JSContext *context, const char **initial_search_path, bool add_standard_search_path) { JS::Value importer_val; JSObject *importer; JS_BeginRequest(context); importer_val = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS); if (G_UNLIKELY (!importer_val.isUndefined())) { gjs_debug(GJS_DEBUG_IMPORTER, "Someone else already created root importer, ignoring second request"); JS_EndRequest(context); return true; } importer = gjs_create_importer(context, "imports", initial_search_path, add_standard_search_path, true, JS::NullPtr()); JS::RootedObject global(context, importer); importer_val = JS::ObjectValue (*importer); JS_DefineFunctions(context, global, &gjs_global_importer_funcs[0]); gjs_set_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS, importer_val); JS_EndRequest(context); return true; } bool gjs_define_root_importer_object(JSContext *context, JS::HandleObject in_object, JS::HandleObject root_importer) { JSAutoRequest ar(context); if (!gjs_object_define_property(context, in_object, GJS_STRING_IMPORTS, root_importer, GJS_MODULE_PROP_FLAGS)) { gjs_debug(GJS_DEBUG_IMPORTER, "DefineProperty imports on %p failed", in_object.get()); return false; } return true; } bool gjs_define_root_importer(JSContext *cx, JS::HandleObject in_object) { JS::Value importer = gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS); JS::RootedObject rooted_importer(cx, &importer.toObject()); return gjs_define_root_importer_object(cx, in_object, rooted_importer); } cjs-3.6.1/cjs/importer.h000066400000000000000000000042531320401450000150620ustar00rootroot00000000000000/* -*- 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 bool gjs_create_root_importer (JSContext *context, const char **initial_search_path, bool add_standard_search_path); bool gjs_define_root_importer(JSContext *cx, JS::HandleObject in_object); bool gjs_define_root_importer_object(JSContext *context, JS::HandleObject in_object, JS::HandleObject root_importer); JSObject *gjs_define_importer(JSContext *context, JS::HandleObject in_object, const char *importer_name, const char **initial_search_path, bool add_standard_search_path); G_END_DECLS #endif /* __GJS_IMPORTER_H__ */ cjs-3.6.1/cjs/jsapi-class.h000066400000000000000000000434061320401450000154350ustar00rootroot00000000000000/* -*- 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 "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) \ 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); \ } \ 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 struct JSClass gjs_##cname##_class = { \ type_name, \ JSCLASS_HAS_PRIVATE | jsclass_flags, \ nullptr, /* addProperty */ \ nullptr, /* deleteProperty */ \ nullptr, /* getProperty */ \ nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ nullptr, /* convert */ \ gjs_##cname##_finalize \ }; \ _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 == NULL) \ 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-3.6.1/cjs/jsapi-constructor-proxy.cpp000066400000000000000000000152501320401450000204230ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * 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. * * Authored by: Philip Chimento */ #include "jsapi-constructor-proxy.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "mem.h" #include "util/log.h" /* This code exposes a __private_GjsConstructorProxy function to JS, which is * approximately equivalent to * * function __private_GjsConstructorProxy(constructor, prototype) { * let my_prototype = prototype; * return new Proxy(constructor, { * getPrototypeOf: function (target) { return my_prototype; }, * }); * } * * but with a C++-only flag that routes all property accesses through the * getPrototypeOf() trap, which may or may not be turned on in JS proxies, * I'm not sure. * * COMPAT: SpiderMonkey doesn't support the getPrototypeOf() trap in JS * proxies yet. That has yet to be released, in the upcoming SpiderMonkey 52. * When that is available, then this whole file can be discontinued. * * That is the reason for the existence of this C++ file, but the reason why it * is needed at all is because of Lang.Class and GObject.Class. We must give * class objects (e.g. "const MyClass = new Lang.Class({...})") a custom * prototype, so that "MyClass instanceof Lang.Class" will be true, and MyClass * will have methods from Class. * * Usually you would give an object a custom prototype using Object.create(), * but that's not possible for function or constructor objects, and MyClass of * course must be a constructor. Previously we solved this with * Object.setPrototypeOf(), but that has performance effects on any code that * uses objects whose prototypes have been altered [1], and SpiderMonkey started * printing conspicuous warnings about it. * * [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf */ static const char constructor_proxy_create_name[] = "__private_GjsConstructorProxy"; /* This char's address is an arbitrary identifier for use in debugging */ static const char constructor_proxy_family = 'p'; enum { SLOT_PROTO, }; /* This class is the C++ equivalent of a proxy handler object. In JS, that is * the second argument passed to the "new Proxy(target, handler)" constructor. */ class GjsConstructorHandler : public js::DirectProxyHandler { static inline JSObject * proto(JS::HandleObject proxy) { return &js::GetProxyExtra(proxy, SLOT_PROTO).toObject(); } public: GjsConstructorHandler() : js::DirectProxyHandler(&constructor_proxy_family, true /* hasPrototype */) { } bool getPrototypeOf(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleObject proto_p) const override { proto_p.set(proto(proxy)); return true; } /* This is called when the associated proxy object is finalized, not the * handler itself */ void finalize(JSFreeOp *fop, JSObject *proxy) const override { GJS_DEC_COUNTER(constructor_proxy); gjs_debug_lifecycle(GJS_DEBUG_PROXY, "constructor proxy %p destroyed", proxy); } bool isCallable(JSObject *obj) const override { return true; } bool isConstructor(JSObject *obj) const override { return true; } static GjsConstructorHandler& singleton(void) { static GjsConstructorHandler the_singleton; return the_singleton; } }; /* Visible to JS as __private_GjsConstructorProxy(constructor, prototype) */ static bool create_gjs_constructor_proxy(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (args.length() < 2) { gjs_throw(cx, "Expected 2 arguments to %s, got %d", constructor_proxy_create_name, args.length()); return false; } if (!args[0].isObject() || !JS::IsConstructor(&args[0].toObject())) { gjs_throw(cx, "First argument must be a constructor"); return false; } if (!args[1].isObject()) { gjs_throw(cx, "Second argument must be a prototype object"); return false; } JS::RootedObject proxy(cx, js::NewProxyObject(cx, &GjsConstructorHandler::singleton(), args[0], &args[1].toObject(), nullptr)); /* We stick this extra object into one of the proxy object's "extra slots", * even though it is private data of the proxy handler. This is because * proxy handlers cannot have trace callbacks. The proxy object does have a * built-in trace callback which traces the "extra slots", so this object * will be kept alive. This also means the handler has no private state at * all, so it can be a singleton. */ js::SetProxyExtra(proxy, SLOT_PROTO, args[1]); args.rval().setObject(*proxy); GJS_INC_COUNTER(constructor_proxy); gjs_debug_lifecycle(GJS_DEBUG_PROXY, "created constructor proxy %p", proxy.get()); return true; } bool gjs_define_constructor_proxy_factory(JSContext *cx) { bool found; JS::RootedObject global(cx, gjs_get_import_global(cx)); if (!JS_HasProperty(cx, global, constructor_proxy_create_name, &found)) return false; if (found) return true; if (!JS_DefineFunction(cx, global, constructor_proxy_create_name, create_gjs_constructor_proxy, 2, JSPROP_READONLY | JSPROP_PERMANENT)) return false; gjs_debug(GJS_DEBUG_PROXY, "Initialized constructor proxy factory"); return true; } cjs-3.6.1/cjs/jsapi-constructor-proxy.h000066400000000000000000000026471320401450000200760ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * 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. */ #ifndef GJS_JSAPI_CONSTRUCTOR_PROXY_H #define GJS_JSAPI_CONSTRUCTOR_PROXY_H #include #include "jsapi-wrapper.h" G_BEGIN_DECLS bool gjs_define_constructor_proxy_factory(JSContext *cx); G_END_DECLS #endif /* GJS_JSAPI_CONSTRUCTOR_PROXY_H */ cjs-3.6.1/cjs/jsapi-dynamic-class.cpp000066400000000000000000000144001320401450000174020ustar00rootroot00000000000000/* -*- 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 "jsapi-private.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); JS::RootedObject global(context, gjs_get_import_global(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 try to search for clasp prototype in the * global object, which is wrong, but it's not a problem because * it will fallback to Object.prototype if the clasp's * constructor is not found (and it won't be found, because we * never call JS_InitClass). */ prototype.set(JS_NewObject(context, clasp)); } if (!prototype) goto out; 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, global, 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 (!JS_LinkConstructorAndPrototype(context, constructor, prototype)) 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, JS_STUBGETTER, JS_STUBSETTER)) 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, "TypeError", NULL, "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-3.6.1/cjs/jsapi-private.cpp000066400000000000000000000047761320401450000163440ustar00rootroot00000000000000/* -*- 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 #include "jsapi-util.h" #include "jsapi-private.h" #include "jsapi-wrapper.h" void gjs_error_reporter(JSContext *context, const char *message, JSErrorReport *report) { const char *warning; GLogLevelFlags level; 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, message); } cjs-3.6.1/cjs/jsapi-private.h000066400000000000000000000032651320401450000160010ustar00rootroot00000000000000/* -*- 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. */ /* This file wraps C++ stuff from the spidermonkey private API so we * can use it from our other C files. This file should be included by * jsapi-util.c only. "Public" API from this jsapi-private.c should be * declared in jsapi-util.h */ #ifndef __GJS_JSAPI_PRIVATE_H__ #define __GJS_JSAPI_PRIVATE_H__ #include #include "cjs/jsapi-util.h" G_BEGIN_DECLS void gjs_schedule_gc_if_needed (JSContext *context); void gjs_gc_if_needed (JSContext *context); G_END_DECLS #endif /* __GJS_JSAPI_PRIVATE_H__ */ cjs-3.6.1/cjs/jsapi-util-args.h000066400000000000000000000340331320401450000162330ustar00rootroot00000000000000/* -*- 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" 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? */ 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? */ 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()); } static inline void assign(JSContext *cx, char c, bool nullable, JS::HandleValue value, char **ref) { if (nullable && (c == 's' || c == 'F') && value.isNull()) { *ref = NULL; return; } if (c == 's') { if (!gjs_string_to_utf8(cx, value, ref)) throw g_strdup("Couldn't convert to string"); } else if (c == 'F') { if (!gjs_string_to_filename(cx, value, ref)) throw g_strdup("Couldn't convert to filename"); } else { throw g_strdup_printf("Wrong type for %c, got char**", c); } } 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"); } 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; } 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"); } 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> 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) {} 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); } static inline void free_if_necessary(char **param_ref) { g_free(*param_ref); } 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 char **) * F: A string, converted into "filename encoding" (i.e. active locale) (pass * a char **) * 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-3.6.1/cjs/jsapi-util-error.cpp000066400000000000000000000140471320401450000167660ustar00rootroot00000000000000/* -*- 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 #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, const char *error_class, 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, -1, error_args[0])) { JS_ReportError(context, "Failed to copy exception string"); goto out; } if (!JS_GetProperty(context, global, error_class, &v_constructor) || !v_constructor.isObject()) { JS_ReportError(context, "??? Missing Error constructor in global object?"); goto out; } /* throw new Error(message) */ constructor = &v_constructor.toObject(); new_exc = JS_New(context, constructor, error_args); if (new_exc == NULL) goto out; if (error_name != NULL) { JS::RootedValue name_value(context); if (!gjs_string_from_utf8(context, error_name, -1, &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_ReportError(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, "Error", NULL, 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 *context, const char *error_class, const char *error_name, const char *format, ...) { va_list args; va_start(args, format); gjs_throw_valist(context, error_class, 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); } cjs-3.6.1/cjs/jsapi-util-root.h000066400000000000000000000244721320401450000162700ustar00rootroot00000000000000/* -*- 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 void trace(JSTracer *tracer, JS::Heap *thing, const char *name); static bool update_after_gc(JS::Heap *location); }; template<> struct GjsHeapOperation { static void trace(JSTracer *tracer, JS::Heap *thing, const char *name) { JS_CallObjectTracer(tracer, thing, name); } static bool update_after_gc(JS::Heap *location) { JS_UpdateWeakPointerAfterGC(location); return (*location == nullptr); } }; template<> struct GjsHeapOperation { static void trace(JSTracer *tracer, JS::Heap *thing, const char *name) { JS_CallValueTracer(tracer, thing, name); } }; /* 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); } /* 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::GCMethods::initial()); m_rooted = true; m_cx = cx; m_notify = notify; m_data = data; m_root = new JS::PersistentRooted(m_cx, thing); 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; } void reset(void) { debug("reset()"); if (!m_rooted) { m_heap = js::GCMethods::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); GjsHeapOperation::trace(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-3.6.1/cjs/jsapi-util-string.cpp000066400000000000000000000273061320401450000171450ustar00rootroot00000000000000/* -*- 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 "jsapi-util.h" #include "jsapi-wrapper.h" bool gjs_string_to_utf8 (JSContext *context, const JS::Value value, char **utf8_string_p) { gsize len; char *bytes; JS_BeginRequest(context); if (!value.isString()) { gjs_throw(context, "Value is not a string, cannot convert to UTF-8"); JS_EndRequest(context); return false; } JS::RootedString str(context, value.toString()); len = JS_GetStringEncodingLength(context, str); if (len == (gsize)(-1)) { JS_EndRequest(context); return false; } if (utf8_string_p) { bytes = JS_EncodeStringToUTF8(context, str); *utf8_string_p = bytes; } JS_EndRequest(context); return true; } bool gjs_string_from_utf8(JSContext *context, const char *utf8_string, ssize_t n_bytes, JS::MutableHandleValue value_p) { char16_t *u16_string; glong u16_string_length; GError *error; /* intentionally using n_bytes even though glib api suggests n_chars; with * n_chars (from g_utf8_strlen()) the result appears truncated */ error = NULL; u16_string = reinterpret_cast(g_utf8_to_utf16(utf8_string, n_bytes, NULL, &u16_string_length, &error)); if (!u16_string) { gjs_throw(context, "Failed to convert UTF-8 string to " "JS string: %s", error->message); g_error_free(error); return false; } JS_BeginRequest(context); /* Avoid a copy - assumes that g_malloc == js_malloc == malloc */ JS::RootedString str(context, JS_NewUCString(context, u16_string, u16_string_length)); if (str) value_p.setString(str); JS_EndRequest(context); return str != NULL; } bool gjs_string_to_filename(JSContext *context, const JS::Value filename_val, char **filename_string_p) { GError *error; gchar *tmp, *filename_string; /* 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); g_free(tmp); return false; } *filename_string_p = filename_string; g_free(tmp); 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; gchar *utf8_string; error = NULL; utf8_string = g_filename_to_utf8(filename_string, n_bytes, NULL, &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); g_free(utf8_string); return false; } if (!gjs_string_from_utf8(context, utf8_string, written, value_p)) return false; g_free(utf8_string); return true; } /* 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 * @value: a JS::Value * @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 the JSString * contained in @value. * Throws a JS exception if value is not a string. * * Returns: false if exception thrown **/ bool gjs_string_get_char16_data(JSContext *context, JS::Value value, char16_t **data_p, size_t *len_p) { JSAutoRequest ar(context); if (!value.isString()) { gjs_throw(context, "Value is not a string, can't return binary data from it"); return false; } if (JS_StringHasLatin1Chars(value.toString())) return from_latin1(context, value.toString(), 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, value.toString(), 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 * @value: JS::Value containing a string * @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::HandleValue value, gunichar **ucs4_string_p, size_t *len_p) { if (ucs4_string_p == NULL) return true; if (!value.isString()) { gjs_throw(cx, "Value is not a string, cannot convert to UCS-4"); return false; } JSAutoRequest ar(cx); JS::RootedString str(cx, value.toString()); size_t len; GError *error = NULL; if (JS_StringHasLatin1Chars(str)) return from_latin1(cx, value.toString(), 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 == NULL) { 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, char **name_p) { JS::RootedValue id_val(context); if (!JS_IdToValue(context, id, &id_val)) return false; if (id_val.isString()) { return gjs_string_to_utf8(context, id_val, name_p); } else { *name_p = NULL; 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) { char *utf8_str; if (gjs_string_to_utf8(context, value, &utf8_str)) { *result = g_utf8_get_char(utf8_str); g_free(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_InternString(cx, string)); JS::RootedId id(cx, INTERNED_STRING_TO_JSID(cx, str)); return id; } cjs-3.6.1/cjs/jsapi-util.cpp000066400000000000000000000710101320401450000156300ustar00rootroot00000000000000/* -*- 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 #include #include "jsapi-class.h" #include "jsapi-util.h" #include "jsapi-wrapper.h" #include "context-private.h" #include "jsapi-private.h" #include #include #include GQuark gjs_util_error_quark (void) { return g_quark_from_static_string ("gjs-util-error-quark"); } static JSClass global_class = { "GjsGlobal", JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST) | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ NULL, /* convert */ NULL, /* finalize */ NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ JS_GlobalObjectTraceHook }; /** * gjs_init_context_standard: * @context: a #JSContext * @global_out: (out): The created global object. * This function creates a global object given the context, * and initializes it with the default API. * * Returns: true on success, false otherwise */ bool gjs_init_context_standard (JSContext *context, JS::MutableHandleObject global) { JS::CompartmentOptions compartment_options; bool extra_warnings = false; if (!g_getenv("GJS_DISABLE_EXTRA_WARNINGS")) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling extra warnings"); extra_warnings = true; } /* setDontReportUncaught: Don't send exceptions to our error report handler; * instead leave them set. This allows us to get at the exception object. * * setExtraWarnings: Report warnings to error reporter function. */ JS::ContextOptionsRef(context).setDontReportUncaught(true); JS::RuntimeOptionsRef(context).setExtraWarnings(extra_warnings); if (!g_getenv("GJS_DISABLE_JIT")) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT"); JS::RuntimeOptionsRef(context) .setIon(true) .setBaseline(true) .setAsmJS(true); } compartment_options.setVersion(JSVERSION_LATEST); global.set(JS_NewGlobalObject(context, &global_class, NULL, JS::FireOnNewGlobalHook, compartment_options)); if (global == NULL) return false; JSAutoCompartment ac(context, global); if (!JS_InitStandardClasses(context, global)) return false; if (!JS_InitReflect(context, global)) return false; if (!JS_DefineDebuggerObject(context, global)) return false; return true; } void gjs_set_global_slot (JSContext *context, GjsGlobalSlot slot, JS::Value value) { JSObject *global; global = gjs_get_import_global(context); JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value); } JS::Value gjs_get_global_slot (JSContext *context, GjsGlobalSlot slot) { JSObject *global; global = gjs_get_import_global(context); return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot); } 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, JSNative getter, JSNative setter) { return JS_DefinePropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value, flags, getter, setter); } bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleObject value, unsigned flags, JSNative getter, JSNative setter) { return JS_DefinePropertyById(cx, obj, gjs_context_get_const_string(cx, property_name), value, flags, getter, setter); } 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 */ char *name; gjs_get_string_id(cx, property_name, &name); if (description) gjs_throw(cx, "No property '%s' in %s (or %s)", name, description, reason); else gjs_throw(cx, "No property '%s' in object %p (or %s)", name, obj.address(), reason); g_free(name); } /* 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, char **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); elems.reserve(array_length); for (i = 0; i < array_length; ++i) { JS::RootedValue element(context, JS::StringValue(JS_NewStringCopyZ(context, array_values[i]))); elems.append(element); } 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 == NULL) return NULL; if (!JS_DefineProperty(context, in_object, array_name, array, attrs)) return NULL; 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, JSString *string) { GString *buf = g_string_new(""); char *chars; JS_BeginRequest(context); g_string_append_c(buf, '"'); if (!gjs_string_to_utf8(context, JS::StringValue(string), &chars)) { /* I'm not sure this code will actually ever be reached, since * JS_EncodeStringToUTF8(), called internally by * gjs_string_to_utf8(), seems to happily output non-valid UTF-8 * bytes. However, let's leave this in, since SpiderMonkey may * decide to do this 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_free(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 = NULL; 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 == NULL) 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 == NULL) 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()) { return gjs_string_readable(context, value.toString()); } JS_BeginRequest(context); JS::RootedString str(context, JS::ToString(context, value)); if (str == NULL) { 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 == NULL) { 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 != NULL); 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) { char *utf8_exception = NULL; JS::RootedString exc_str(cx, JS::ToString(cx, exc)); if (exc_str != NULL) gjs_string_to_utf8(cx, JS::StringValue(exc_str), &utf8_exception); return utf8_exception; } bool gjs_log_exception_full(JSContext *context, JS::HandleValue exc, JS::HandleString message) { char *utf8_exception, *utf8_message; 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 { JS::RootedValue js_name(context); char *utf8_name; if (gjs_object_get_property(context, exc_obj, GJS_STRING_NAME, &js_name) && js_name.isString() && gjs_string_to_utf8(context, js_name, &utf8_name)) { is_syntax = strcmp("SyntaxError", utf8_name) == 0; } utf8_exception = utf8_exception_from_non_gerror_value(context, exc); } } if (message != NULL) gjs_string_to_utf8(context, JS::StringValue(message), &utf8_message); else utf8_message = NULL; /* 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; char *utf8_fileName; 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); if (js_fileName.isString()) gjs_string_to_utf8(context, js_fileName, &utf8_fileName); else utf8_fileName = g_strdup("unknown"); lineNumber = js_lineNumber.toInt32(); if (utf8_message) { g_critical("JS ERROR: %s: %s @ %s:%u", utf8_message, utf8_exception, utf8_fileName, lineNumber); } else { g_critical("JS ERROR: %s @ %s:%u", utf8_exception, utf8_fileName, lineNumber); } g_free(utf8_fileName); } else { char *utf8_stack; JS::RootedValue stack(context); if (exc.isObject() && gjs_object_get_property(context, exc_obj, GJS_STRING_STACK, &stack) && stack.isString()) gjs_string_to_utf8(context, stack, &utf8_stack); else utf8_stack = NULL; if (utf8_message) { if (utf8_stack) g_warning("JS ERROR: %s: %s\n%s", utf8_message, utf8_exception, utf8_stack); else g_warning("JS ERROR: %s: %s", utf8_message, utf8_exception); } else { if (utf8_stack) g_warning("JS ERROR: %s\n%s", utf8_exception, utf8_stack); else g_warning("JS ERROR: %s", utf8_exception); } g_free(utf8_stack); } g_free(utf8_exception); g_free(utf8_message); 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, JS::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; } /* get a debug string for type tag in JS::Value */ const char* gjs_get_type_name(JS::Value value) { if (value.isNull()) { return "null"; } else if (value.isUndefined()) { return "undefined"; } else if (value.isInt32()) { return "integer"; } else if (value.isDouble()) { return "double"; } else if (value.isBoolean()) { return "boolean"; } else if (value.isString()) { return "string"; } else if (value.isObject()) { return "object"; } else { return ""; } } #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 gint64 last_gc_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_time < 5 * 16666) return; _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_GC(JS_GetRuntime(context)); last_gc_time = now; } 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.setUTF8(true) .setFileAndLine(filename, start_line_number) .setSourceIsLazy(true); JS::RootedScript compiled_script(context); if (!JS::Compile(context, object, options, script, real_len, &compiled_script)) return false; JS::AutoObjectVector scope_chain(context); scope_chain.append(eval_obj); if (!JS_ExecuteScript(context, scope_chain, compiled_script, 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-3.6.1/cjs/jsapi-util.h000066400000000000000000000377331320401450000153130ustar00rootroot00000000000000/* -*- 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 "jsapi-wrapper.h" #include "cjs/runtime.h" #include "gi/gtype.h" class GjsAutoChar : public std::unique_ptr { public: typedef std::unique_ptr U; GjsAutoChar(char *str = nullptr) : U(str, g_free) {} operator const char *() { return get(); } void operator= (const char* str) { reset(const_cast(str)); } }; template class GjsAutoUnref : public std::unique_ptr { public: typedef std::unique_ptr U; GjsAutoUnref(T *ptr = nullptr) : U(ptr, g_object_unref) {} operator T *() { return U::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 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; 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. * * READONLY: forbid setting prop to another value * 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()) bool gjs_init_context_standard(JSContext *context, JS::MutableHandleObject global); JSObject* gjs_get_import_global (JSContext *context); JS::Value gjs_get_global_slot (JSContext *context, GjsGlobalSlot slot); void gjs_set_global_slot (JSContext *context, GjsGlobalSlot slot, JS::Value value); 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, const char *error_class, 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_error_reporter (JSContext *context, const char *message, JSErrorReport *report); bool gjs_string_to_utf8 (JSContext *context, const JS::Value string_val, char **utf8_string_p); bool gjs_string_from_utf8(JSContext *context, const char *utf8_string, ssize_t n_bytes, JS::MutableHandleValue value_p); bool gjs_string_to_filename (JSContext *context, const JS::Value string_val, char **filename_string_p); 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 *context, JS::Value value, char16_t **data_p, size_t *len_p); bool gjs_string_to_ucs4(JSContext *cx, JS::HandleValue 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, char **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); const char* gjs_get_type_name (JS::Value value); /* Functions intended for more "internal" use */ void gjs_maybe_gc (JSContext *context); bool gjs_context_get_frame_info(JSContext *cx, mozilla::Maybe stack, mozilla::Maybe fileName, mozilla::Maybe lineNumber); 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_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 bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleValue value, unsigned flags, JSNative getter = nullptr, JSNative setter = nullptr); bool gjs_object_define_property(JSContext *cx, JS::HandleObject obj, GjsConstString property_name, JS::HandleObject value, unsigned flags, JSNative getter = nullptr, JSNative setter = nullptr); 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, char **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); } #endif /* __GJS_JSAPI_UTIL_H__ */ cjs-3.6.1/cjs/jsapi-wrapper.h000066400000000000000000000034471320401450000160110ustar00rootroot00000000000000/* -*- 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 /* SpiderMonkey's #defines that affect public API */ /* COMPAT: SpiderMonkey headers in some places use DEBUG instead of JS_DEBUG */ /* https://bugzilla.mozilla.org/show_bug.cgi?id=1261161 */ #if defined(JS_DEBUG) && JS_DEBUG #define DEBUG 1 #endif #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) #pragma GCC system_header #endif #include #include #include #include /* For jsapi-constructor-proxy */ #endif /* GJS_JSAPI_WRAPPER_H */ cjs-3.6.1/cjs/macros.h000066400000000000000000000026501320401450000145040ustar00rootroot00000000000000/* -*- 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-3.6.1/cjs/mem.cpp000066400000000000000000000065271320401450000143400ustar00rootroot00000000000000/* -*- 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(gerror) GJS_DEFINE_COUNTER(closure) GJS_DEFINE_COUNTER(database) GJS_DEFINE_COUNTER(function) GJS_DEFINE_COUNTER(fundamental) GJS_DEFINE_COUNTER(importer) GJS_DEFINE_COUNTER(ns) GJS_DEFINE_COUNTER(object) GJS_DEFINE_COUNTER(param) GJS_DEFINE_COUNTER(repo) GJS_DEFINE_COUNTER(resultset) GJS_DEFINE_COUNTER(weakhash) GJS_DEFINE_COUNTER(interface) GJS_DEFINE_COUNTER(constructor_proxy) #define GJS_LIST_COUNTER(name) \ & gjs_counter_ ## name static GjsMemCounter* counters[] = { GJS_LIST_COUNTER(boxed), GJS_LIST_COUNTER(gerror), GJS_LIST_COUNTER(closure), GJS_LIST_COUNTER(database), GJS_LIST_COUNTER(function), GJS_LIST_COUNTER(fundamental), GJS_LIST_COUNTER(importer), GJS_LIST_COUNTER(ns), GJS_LIST_COUNTER(object), GJS_LIST_COUNTER(param), GJS_LIST_COUNTER(repo), GJS_LIST_COUNTER(resultset), GJS_LIST_COUNTER(weakhash), GJS_LIST_COUNTER(interface), GJS_LIST_COUNTER(constructor_proxy), }; 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-3.6.1/cjs/mem.h000066400000000000000000000051301320401450000137720ustar00rootroot00000000000000/* -*- 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(gerror) GJS_DECLARE_COUNTER(closure) GJS_DECLARE_COUNTER(database) GJS_DECLARE_COUNTER(function) GJS_DECLARE_COUNTER(fundamental) GJS_DECLARE_COUNTER(importer) GJS_DECLARE_COUNTER(ns) GJS_DECLARE_COUNTER(object) GJS_DECLARE_COUNTER(param) GJS_DECLARE_COUNTER(repo) GJS_DECLARE_COUNTER(resultset) GJS_DECLARE_COUNTER(weakhash) GJS_DECLARE_COUNTER(interface) GJS_DECLARE_COUNTER(constructor_proxy) #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-3.6.1/cjs/native.cpp000066400000000000000000000063661320401450000150510ustar00rootroot00000000000000/* -*- 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_import_native_module: * @context: * @module_obj: * * Return a native module that's been preloaded. */ bool gjs_import_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-3.6.1/cjs/native.h000066400000000000000000000042071320401450000145060ustar00rootroot00000000000000/* -*- 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.c to load a statically linked native module */ bool gjs_import_native_module (JSContext *context, const char *name, JS::MutableHandleObject module_out); G_END_DECLS #endif /* __GJS_NATIVE_H__ */ cjs-3.6.1/cjs/runtime.cpp000066400000000000000000000245741320401450000152470ustar00rootroot00000000000000/* -*- 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-util.h" #include "jsapi-wrapper.h" #include "runtime.h" #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN #include #endif struct RuntimeData { unsigned refcount; bool in_gc_sweep; }; bool gjs_runtime_is_sweeping (JSRuntime *runtime) { RuntimeData *data = (RuntimeData*) JS_GetRuntimePrivate(runtime); return data->in_gc_sweep; } /* 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) { bool success = false; char *utf8 = NULL; char *upper_case_utf8 = NULL; if (!gjs_string_to_utf8(context, JS::StringValue(src), &utf8)) goto out; upper_case_utf8 = g_utf8_strup (utf8, -1); if (!gjs_string_from_utf8(context, upper_case_utf8, -1, retval)) goto out; success = true; out: g_free(utf8); g_free(upper_case_utf8); return success; } static bool gjs_locale_to_lower_case (JSContext *context, JS::HandleString src, JS::MutableHandleValue retval) { bool success = false; char *utf8 = NULL; char *lower_case_utf8 = NULL; if (!gjs_string_to_utf8(context, JS::StringValue(src), &utf8)) goto out; lower_case_utf8 = g_utf8_strdown (utf8, -1); if (!gjs_string_from_utf8(context, lower_case_utf8, -1, retval)) goto out; success = true; out: g_free(utf8); g_free(lower_case_utf8); return success; } static bool gjs_locale_compare (JSContext *context, JS::HandleString src_1, JS::HandleString src_2, JS::MutableHandleValue retval) { bool success = false; char *utf8_1 = NULL, *utf8_2 = NULL; if (!gjs_string_to_utf8(context, JS::StringValue(src_1), &utf8_1) || !gjs_string_to_utf8(context, JS::StringValue(src_2), &utf8_2)) goto out; retval.setInt32(g_utf8_collate(utf8_1, utf8_2)); success = true; out: g_free(utf8_1); g_free(utf8_2); return success; } static bool gjs_locale_to_unicode (JSContext *context, const char *src, JS::MutableHandleValue retval) { bool success; char *utf8; GError *error = NULL; 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; } success = gjs_string_from_utf8(context, utf8, -1, retval); g_free (utf8); return success; } static void destroy_runtime(gpointer data) { JSRuntime *runtime = (JSRuntime *) data; RuntimeData *rtdata = (RuntimeData *) JS_GetRuntimePrivate(runtime); JS_DestroyRuntime(runtime); g_free(rtdata); } static GPrivate thread_runtime = G_PRIVATE_INIT(destroy_runtime); 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 *user_data) { RuntimeData *data = static_cast(user_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) data->in_gc_sweep = true; else if (status == JSFINALIZE_GROUP_END) data->in_gc_sweep = false; } /* Destroys the current thread's runtime regardless of refcount. No-op if there * is no runtime */ static void gjs_destroy_runtime_for_current_thread(void) { g_private_replace(&thread_runtime, NULL); } #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: gjs_destroy_runtime_for_current_thread(); JS_ShutDown (); break; default: /* do nothing */ ; } return TRUE; } #else class GjsInit { public: GjsInit() { if (!JS_Init()) g_error("Could not initialize Javascript"); } ~GjsInit() { /* No-op if the runtime was already destroyed */ gjs_destroy_runtime_for_current_thread(); JS_ShutDown(); } operator bool() { return true; } }; static GjsInit gjs_is_inited; #endif static JSRuntime * gjs_runtime_for_current_thread(void) { JSRuntime *runtime = (JSRuntime *) g_private_get(&thread_runtime); RuntimeData *data; if (!runtime) { g_assert(gjs_is_inited); runtime = JS_NewRuntime(32 * 1024 * 1024 /* max bytes */); if (runtime == NULL) g_error("Failed to create javascript runtime"); data = g_new0(RuntimeData, 1); JS_SetRuntimePrivate(runtime, data); // commented are defaults in moz-24 JS_SetNativeStackQuota(runtime, 1024*1024); JS_SetGCParameter(runtime, JSGC_MAX_MALLOC_BYTES, 128*1024*1024); JS_SetGCParameter(runtime, JSGC_MAX_BYTES, -1); JS_SetGCParameter(runtime, JSGC_MODE, JSGC_MODE_INCREMENTAL); JS_SetGCParameter(runtime, JSGC_SLICE_TIME_BUDGET, 10); /* ms */ // JS_SetGCParameter(runtime, JSGC_HIGH_FREQUENCY_TIME_LIMIT, 1000); /* ms */ JS_SetGCParameter(runtime, JSGC_DYNAMIC_MARK_SLICE, true); JS_SetGCParameter(runtime, JSGC_DYNAMIC_HEAP_GROWTH, true); // JS_SetGCParameter(runtime, JSGC_LOW_FREQUENCY_HEAP_GROWTH, 150); // JS_SetGCParameter(runtime, JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, 150); // JS_SetGCParameter(runtime, JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, 300); // JS_SetGCParameter(runtime, JSGC_HIGH_FREQUENCY_LOW_LIMIT, 100); // JS_SetGCParameter(runtime, JSGC_HIGH_FREQUENCY_HIGH_LIMIT, 500); // JS_SetGCParameter(runtime, JSGC_ALLOCATION_THRESHOLD, 30); // JS_SetGCParameter(runtime, JSGC_DECOMMIT_THRESHOLD, 32); JS_SetLocaleCallbacks(runtime, &gjs_locale_callbacks); JS_AddFinalizeCallback(runtime, gjs_finalize_callback, data); JS_SetErrorReporter(runtime, gjs_error_reporter); g_private_set(&thread_runtime, runtime); } return runtime; } /* These two act on the current thread's runtime. In the future they will go * away because SpiderMonkey is going to merge JSContext and JSRuntime. */ /* Creates a new runtime with one reference if there is no runtime yet */ JSRuntime * gjs_runtime_ref(void) { JSRuntime *rt = static_cast(gjs_runtime_for_current_thread()); RuntimeData *data = static_cast(JS_GetRuntimePrivate(rt)); g_atomic_int_inc(&data->refcount); return rt; } /* No-op if there is no runtime */ void gjs_runtime_unref(void) { JSRuntime *rt = static_cast(g_private_get(&thread_runtime)); if (rt == NULL) return; RuntimeData *data = static_cast(JS_GetRuntimePrivate(rt)); if (g_atomic_int_dec_and_test(&data->refcount)) gjs_destroy_runtime_for_current_thread(); } cjs-3.6.1/cjs/runtime.h000066400000000000000000000026571320401450000147120ustar00rootroot00000000000000/* -*- 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_RUNTIME_H__ #define __GJS_RUNTIME_H__ #include JSRuntime *gjs_runtime_ref(void); void gjs_runtime_unref(void); bool gjs_runtime_is_sweeping (JSRuntime *runtime); #endif /* __GJS_RUNTIME_H__ */ cjs-3.6.1/cjs/stack.cpp000066400000000000000000000104741320401450000146630ustar00rootroot00000000000000/* 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" bool gjs_context_get_frame_info(JSContext *context, mozilla::Maybe stack, mozilla::Maybe fileName, mozilla::Maybe lineNumber) { JSAutoRequest ar(context); JS::RootedObject global(context, JS::CurrentGlobalOrNull(context)), constructor(context); JSAutoCompartment ac(context, global); JS::RootedId error_id(context, gjs_intern_string_to_id(context, "Error")); if (!gjs_object_require_property(context, global, "global object", error_id, &constructor)) return false; JS::RootedObject err_obj(context, JS_New(context, constructor, JS::HandleValueArray::empty())); if (stack && !gjs_object_get_property(context, err_obj, GJS_STRING_STACK, stack.ref())) return false; if (fileName && !gjs_object_get_property(context, err_obj, GJS_STRING_FILENAME, fileName.ref())) return false; if (lineNumber && !gjs_object_get_property(context, err_obj, GJS_STRING_LINE_NUMBER, lineNumber.ref())) return false; return true; } void gjs_context_print_stack_stderr(GjsContext *context) { JSContext *cx = (JSContext*) gjs_context_get_native_context(context); JS::RootedValue v_stack(cx); char *stack; g_printerr("== Stack trace for context %p ==\n", context); /* Stderr is locale encoding, so we use string_to_filename here */ if (!gjs_context_get_frame_info(cx, mozilla::Some(&v_stack), mozilla::Nothing(), mozilla::Nothing()) || !gjs_string_to_filename(cx, v_stack, &stack)) { g_printerr("No stack trace available\n"); return; } g_printerr("%s\n", stack); g_free(stack); } 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-3.6.1/cjs/type-module.cpp000066400000000000000000000044021320401450000160140ustar00rootroot00000000000000/* -*- 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-3.6.1/cjs/type-module.h000066400000000000000000000041331320401450000154620ustar00rootroot00000000000000/* -*- 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-3.6.1/configure.ac000066400000000000000000000231551320401450000145610ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. m4_define(pkg_major_version, 3) m4_define(pkg_minor_version, 6) m4_define(pkg_micro_version, 1) 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_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 m4_ifdef([AX_CODE_COVERAGE], [AX_CODE_COVERAGE], [AM_CONDITIONAL(CODE_COVERAGE_ENABLED, false)]) 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-38" 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])])]) 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 this configure option, then GJS needs to dnl be compiled with it as well, because we inherit from a SpiderMonkey class in dnl jsapi-constructor-proxy.cpp. See build/autoconf/compiler-opts.m4 in mozjs31. AC_ARG_ENABLE([cpp-rtti], [AS_HELP_STRING([--enable-cpp-rtti], [needs to match SpiderMonkey's config option @<:@default=off@:>@])]) AS_IF([test "x$enable_cpp_rtti" != "xyes"], [AX_APPEND_COMPILE_FLAGS([-fno-rtti])], [AX_APPEND_COMPILE_FLAGS([-GR-])]) 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"]) AX_APPEND_COMPILE_FLAGS(['--param inline-unit-growth=50']) 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} Run tests under: ${TEST_MSG} Code coverage: ${enable_code_coverage} ]) cjs-3.6.1/doc/000077500000000000000000000000001320401450000130325ustar00rootroot00000000000000cjs-3.6.1/doc/ByteArray.md000066400000000000000000000150651320401450000152650ustar00rootroot00000000000000The 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-3.6.1/doc/Class_Framework.md000066400000000000000000000125021320401450000164360ustar00rootroot00000000000000# Class framework # Keep in mind that JavaScript does not "really" have classes in the sense of C++ or Java; you can't create new types beyond the built-in ones (Object, Array, RegExp, String). However, you can create object instances that share common properties, including methods, using the prototype mechanism. Every JavaScript implementation invents its own syntactic sugar for doing this, and so did we, but the basics are always the same. In ES6, JavaScript finally went ahead and standardized some particular syntactic sugar, which GJS will support in the future. Each JavaScript object has a property `__proto__`; if you write `obj.foo` and `foo` is not in `obj`, JavaScript will look for `foo` in `__proto__`. If several objects have the same `__proto__`, then they can share methods or other state. You can create objects with a constructor, which is a special function. Say you have: ```js function Foo() {} let f = new Foo(); ``` For `new Foo()` JavaScript will create a new, empty object; and execute `Foo()` with the new, empty object as `this`. So the function `Foo()` sets up the new object. `new Foo()` will also set `__proto__` on the new object to `Foo.prototype`. The property `prototype` on a constructor is used to initialize `__proto__` for objects the constructor creates. To get the right `__proto__` on objects, we need the right prototype property on the constructor. You could think of `f = new Foo()` as: ```js let f = {}; // create new object f.__proto__ = Foo.prototype; // doing this by hand isn't actually allowed Foo.call(f); // invoke Foo() with new object as "this" ``` Our syntactic sugar is `Lang.Class` which works like this: ```js const Lang = imports.lang; const Foo = new Lang.Class({ Name: 'Foo', _init: function(arg1, arg2) { this._myPrivateInstanceVariable = arg1; }, myMethod: function() { }, myClassVariable: 42, myOtherClassVariable: "Hello" } ``` This pattern means that when you do `let f = new Foo()`, `f` will be a new object, `f.__proto__` will point to `Foo.prototype` which will be the object that you passed to `new Lang.Class()`, and `Foo.prototype._init` will be called to set up the object. > **NOTE:** Again, on the JavaScript language level, Foo is not a class in the sense of Java or C++; it's just a constructor function, which means it's intended for use with the `new Foo()` syntax to create an object. Once the object is created, from a JavaScript language perspective its type is the built-in type `Object` - though we're using it and thinking of it as if it had type `Foo`, JavaScript doesn't have a clue about that and will never do any type-checking based on which constructor was used to create something. All typing is "duck typing." The built-in types, such as `Object`, `String`, `Error`, `RegExp`, and `Array`, _are_ real types, however, and do get type-checked. > **NOTE:** If a constructor function has a return value, it is used as the value of `new Foo()` - replacing the automatically-created `this` object passed in to the constructor. If a constructor function returns nothing (undefined), then the passed-in `this` is used. In general, avoid this feature - constructors should have no return value. But this feature may be necessary if you need the new instance to have a built-in type other than Object. If you return a value from the constructor, `this` is simply discarded, so referring to `this` while in the constructor won't make sense. ## JavaScript "inheritance" ## There are lots of ways to simulate "inheritance" in JavaScript. In general, it's a good idea to avoid class hierarchies in JavaScript. But sometimes it's the best solution. Our preferred approach is to use our syntactic sugar `Lang.Class` which includes an `Extends` property which sets the prototype of the subclass to the base class. Looking up a property in the subclass starts with the properties of the instance. If the property isn't there, then the prototype chain is followed first to the subclass's prototype and then to the base class's prototype. ```js const Lang = imports.lang; const Base = new Lang.Class({ Name: 'Base', _init : function(foo) { this._foo = foo; }, frobate : function() { } }); const Sub = new Lang.Class({ Name: 'Sub', Extends: Base, _init: function(foo, bar) { // here is an example of how to "chain up" this.parent(foo); this._bar = bar; } // add a newMethod property in Sub.prototype newMethod : function() { } }); ``` > **NOTE:** You cannot use this mechanism to inherit from a built-in type, such as String or Error, because the methods on those objects expect them to have primitive type String or Error, while your constructor will create an Object instead. In portable JavaScript code you'll often see a different technique used to get this prototype chain: ```js function Base(foo) ... Base.prototype = ... function Sub(foo, bar) ... // Do NOT do this - do not use an instance of Base as the Sub prototype Sub.prototype = new Base(); ``` The problem with this pattern is that you might want to have side effects in the `Base()` constructor. Say `Base()` in its constructor creates a window on the screen, because `Base()` is a dialog class or something. If you use the pattern that some instances of `Base()` are just prototypes for subclasses, you'll get extra windows on the screen. The other problem with this pattern is that it's just confusing and weird. cjs-3.6.1/doc/Hacking.md000066400000000000000000000063601320401450000147250ustar00rootroot00000000000000# 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['mozjs38'] = '--enable-debug' ``` Make sure it is built first with `jhbuild build mozjs38`, 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-38.0.0/js/src/shell/js-gdb.py ``` ## 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 every once in a while. Download the Valgrind false-positive suppression file from GLib: ```sh wget https://git.gnome.org/browse/glib/plain/glib.supp ``` Run the test suite: ```sh jhbuild shell cd ~/.cache/jhbuild/build/gjs G_DEBUG=gc-friendly G_SLICE=always-malloc ./libtool --mode=execute valgrind --leak-check=yes --suppressions=/path/to/glib.supp ./gjs-tests ``` (And a similar command to run each `minijasmine` test.) ### 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/Jhbuildcjs-3.6.1/doc/SpiderMonkey_Memory.md000066400000000000000000000153611320401450000173230ustar00rootroot00000000000000# 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-3.6.1/doc/Style_Guide.md000066400000000000000000000116201320401450000155710ustar00rootroot00000000000000# 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-3.6.1/doc/cairo.md000066400000000000000000000042461320401450000144570ustar00rootroot00000000000000The 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-3.6.1/examples/000077500000000000000000000000001320401450000141035ustar00rootroot00000000000000cjs-3.6.1/examples/README000066400000000000000000000001151320401450000147600ustar00rootroot00000000000000In order to run those example scripts, do: gjs-console script-filename.js cjs-3.6.1/examples/clutter.js000066400000000000000000000010011320401450000161130ustar00rootroot00000000000000const 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-3.6.1/examples/gettext.js000066400000000000000000000005511320401450000161260ustar00rootroot00000000000000imports.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-3.6.1/examples/gio-cat.js000066400000000000000000000011401320401450000157600ustar00rootroot00000000000000 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-3.6.1/examples/gtk.js000066400000000000000000000044341320401450000152330ustar00rootroot00000000000000imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk; // This is a callback function. The data arguments are ignored // in this example. More on callbacks below. function hello(widget) { log("Hello World"); } function onDeleteEvent(widget, event) { // If you return false in the "delete_event" signal handler, // GTK will emit the "destroy" signal. Returning true means // you don't want the window to be destroyed. // This is useful for popping up 'are you sure you want to quit?' // type dialogs. log("delete event occurred"); // Change false to true and the main window will not be destroyed // with a "delete_event". return false; } function onDestroy(widget) { log("destroy signal occurred"); Gtk.main_quit(); } Gtk.init(null); // create a new window let win = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL }); // 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. win.connect("delete-event", onDeleteEvent); // Here we connect the "destroy" event to a signal handler. // This event occurs when we call gtk_widget_destroy() on the window, // or if we return false in the "onDeleteEvent" callback. win.connect("destroy", onDestroy); // Sets the border width of the window. win.set_border_width(10); // Creates a new button with the label "Hello World". let button = new Gtk.Button({ label: "Hello World" }); // When the button receives the "clicked" signal, it will call the // function hello(). The hello() function is defined above. button.connect("clicked", hello); // This will cause the window to be destroyed by calling // gtk_widget_destroy(window) when "clicked". Again, the destroy // signal could come from here, or the window manager. button.connect("clicked", function() { win.destroy(); }); // This packs the button into the window (a GTK container). win.add(button); // The final step is to display this newly created widget. button.show(); // and the window win.show(); // All gtk applications must have a Gtk.main(). Control ends here // and waits for an event to occur (like a key press or mouse event). Gtk.main(); cjs-3.6.1/examples/http-server.js000066400000000000000000000017131320401450000167260ustar00rootroot00000000000000// 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-3.6.1/examples/test.jpg000066400000000000000000000344201320401450000155670ustar00rootroot00000000000000JFIFHHC     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-3.6.1/examples/webkit.js000066400000000000000000000005141320401450000157260ustar00rootroot00000000000000imports.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-3.6.1/gi/000077500000000000000000000000001320401450000126645ustar00rootroot00000000000000cjs-3.6.1/gi/arg.cpp000066400000000000000000003572631320401450000141610ustar00rootroot00000000000000/* -*- 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 "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 #define HANDLE_STRING(type, lctype) \ case GI_TYPE_TAG_##type: { \ char *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_##lctype(cx, str_val, &cstr)) \ return false; \ *pointer_out = cstr; \ break; \ } HANDLE_STRING(FILENAME, filename); HANDLE_STRING(UTF8, utf8); #undef HANDLE_STRING 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::AutoIdArray ids(context, JS_Enumerate(context, props)); if (!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++) { elems.growBy(1); if (!gjs_string_from_utf8(context, strv[i], -1, elems[i])) return false; } JS::RootedObject obj(context, JS_NewArrayObject(context, elems)); if (obj == NULL) 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; } if (!elem.isString()) { gjs_throw(context, "Invalid element in string array"); g_strfreev(result); return false; } if (!gjs_string_to_utf8(context, elem, (char **)&(result[i]))) { g_strfreev(result); return false; } } *arr_p = result; return true; } static bool gjs_string_to_intarray(JSContext *context, JS::Value string_val, GITypeInfo *param_info, void **arr_p, gsize *length) { GITypeTag element_type; char *result; 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) { if (!gjs_string_to_utf8(context, string_val, &result)) return false; *arr_p = result; *length = strlen(result); return true; } if (element_type == GI_TYPE_TAG_INT16 || element_type == GI_TYPE_TAG_UINT16) { if (!gjs_string_get_char16_data(context, string_val, &result16, length)) return false; *arr_p = result16; return true; } if (element_type == GI_TYPE_TAG_UNICHAR) { gunichar *result_ucs4; JS::RootedValue root(context, string_val); if (!gjs_string_to_ucs4(context, root, &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); elems.resize(length); 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, gjs_get_type_name(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 */ if (!gjs_string_to_intarray(context, value, 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; } 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 = (guint32)i; 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 = v; } 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()) { char *filename_str; if (gjs_string_to_filename(context, value, &filename_str)) // doing this as a separate step to avoid type-punning arg->v_pointer = filename_str; 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()) { char *utf8_str; if (gjs_string_to_utf8(context, value, &utf8_str)) // doing this as a separate step to avoid type-punning arg->v_pointer = utf8_str; 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 (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(), gjs_get_type_name(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", gjs_get_type_name(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; elems.growBy(1); 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; elems.growBy(1); 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 == NULL) 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 == NULL) 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); elems.resize(length); #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 == NULL) 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 == NULL) 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]; \ elems.growBy(1); \ 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 == NULL) 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; char *keyutf8 = NULL; GArgument keyarg, valarg; bool result; // 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 == NULL) return false; value_p.setObject(*obj); JS::RootedValue keyjs(context), valjs(context); JS::RootedString keystr(context); result = false; 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)) goto out; keystr = JS::ToString(context, keyjs); if (!keystr) goto out; if (!gjs_string_to_utf8(context, JS::StringValue(keystr), &keyutf8)) goto out; if (!gjs_value_from_g_argument(context, &valjs, val_param_info, &valarg, true)) goto out; if (!JS_DefineProperty(context, obj, keyutf8, valjs, JSPROP_ENUMERATE)) goto out; g_free(keyutf8); keyutf8 = NULL; } result = true; out: if (keyutf8) g_free(keyutf8); return result; } 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 (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: { JSObject *obj; obj = gjs_gtype_create_gtype_wrapper(context, arg->v_ssize); value_p.setObjectOrNull(obj); } break; case GI_TYPE_TAG_UNICHAR: { char utf8[7]; gint bytes; /* Preserve the bidirectional mapping between 0 and "" */ if (arg->v_uint32 == 0) { return gjs_string_from_utf8 (context, "", 0, value_p); } 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 (context, (char*)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, (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_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) { 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-3.6.1/gi/arg.h000066400000000000000000000116601320401450000136120ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/boxed.cpp000066400000000000000000001304251320401450000144760ustar00rootroot00000000000000/* -*- 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 *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static bool boxed_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Boxed *priv; char *name = NULL; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { g_free(name); return false; /* wrong class */ } if (priv->gboxed == NULL) { /* We are the prototype, so look for methods and other class properties */ GIFunctionInfo *method_info; method_info = g_struct_info_find_method((GIStructInfo*) 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 the Boxed prototype */ if (gjs_define_function(context, obj, priv->gtype, (GICallableInfo *)method_info) == NULL) { g_base_info_unref( (GIBaseInfo*) method_info); g_free(name); return false; } *resolved = true; } else { *resolved = false; } g_base_info_unref( (GIBaseInfo*) method_info); } } else { /* 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; } g_free(name); 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::AutoIdArray ids(context, JS_Enumerate(context, props)); if (!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); JS::RootedId prop_id(context); for (ix = 0, length = ids.length(); ix < length; ix++) { GIFieldInfo *field_info; char *name = NULL; 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, g_base_info_get_name((GIBaseInfo *)priv->info)); g_free(name); return false; } /* 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)) { g_free(name); return false; } if (!boxed_set_field_from_value(context, priv, field_info, value)) { g_free(name); return false; } g_free(name); } 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, JS::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) { bool retval; /* 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 */ JS::RootedId default_constructor_name(context, priv->default_constructor_name); retval = boxed_invoke_constructor(context, obj, default_constructor_name, args); return retval; } 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 == NULL) 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, NULL, 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: * * - Storing 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_new_resolve() would be possible * as well if doing it ahead of time caused to much start-up * memory overhead. */ if (n_fields > 256) { g_warning("Only defining the first 256 fields in boxed type '%s'", g_base_info_get_name ((GIBaseInfo *)priv->info)); n_fields = 256; } 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_CallIdTracer(tracer, &priv->zero_args_constructor_name, "Boxed::zero_args_constructor_name"); JS_CallIdTracer(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. * * 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_HAS_RESERVED_SLOTS(1) | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ boxed_resolve, NULL, /* convert */ boxed_finalize, NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ boxed_trace }; 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, JS::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 == NULL) 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, "TypeError", NULL, "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, "TypeError", NULL, "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, "TypeError", NULL, "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-3.6.1/gi/boxed.h000066400000000000000000000050771320401450000141470ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/closure.cpp000066400000000000000000000230501320401450000150440ustar00rootroot00000000000000/* -*- 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 == NULL) 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 != NULL) { g_assert(c->obj == obj); 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 == NULL) { 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; JS::ExposeObjectToActiveJS(self->obj); 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(); } void gjs_closure_invoke(GClosure *closure, const JS::HandleValueArray& args, JS::MutableHandleValue retval) { Closure *c; JSContext *context; c = &((GjsClosure*) closure)->priv; if (c->obj == NULL) { /* We were destroyed; become a no-op */ c->context = NULL; return; } context = c->context; JS_BeginRequest(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" object; null is some kind of default presumably */ JS::NullPtr(), 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 (!gjs_log_exception(context)) gjs_debug_closure("Closure invocation failed but no exception was set?" "closure %p", closure); goto out; } if (gjs_log_exception(context)) { gjs_debug_closure("Closure invocation succeeded but an exception was set" " - closure %p", closure); } JS_MaybeGC(context); out: JS_EndRequest(context); } 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 == NULL) 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-3.6.1/gi/closure.h000066400000000000000000000041101320401450000145050ustar00rootroot00000000000000/* -*- 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); void gjs_closure_invoke(GClosure *closure, const JS::HandleValueArray& args, JS::MutableHandleValue retval); 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-3.6.1/gi/enumeration.cpp000066400000000000000000000143771320401450000157320ustar00rootroot00000000000000/* -*- 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 == NULL) { 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-3.6.1/gi/enumeration.h000066400000000000000000000036041320401450000153660ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/foreign.cpp000066400000000000000000000143721320401450000150300ustar00rootroot00000000000000/* -*- 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) { int code; GError *error = NULL; 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); if (!gjs_context_eval((GjsContext*) JS_GetContextPrivate(context), script, strlen(script), "", &code, &error)) { g_printerr("ERROR: %s\n", error->message); g_free(script); g_error_free(error); 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-3.6.1/gi/foreign.h000066400000000000000000000076131320401450000144750ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/function.cpp000066400000000000000000002051311320401450000152170ustar00rootroot00000000000000/* -*- 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-private.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_callable_info_free_closure(trampoline->info, trampoline->closure); g_base_info_unref( (GIBaseInfo*) trampoline->info); g_free (trampoline->param_types); trampoline->~GjsCallbackTrampoline(); 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; } } /* 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 **args, void *data) { JSContext *context; JSRuntime *runtime; JSObject *func_obj; GjsCallbackTrampoline *trampoline; int i, n_args, n_jsargs, n_outargs; GITypeInfo ret_type; bool success = false; bool ret_type_is_void; trampoline = (GjsCallbackTrampoline *) data; g_assert(trampoline); gjs_callback_trampoline_ref(trampoline); context = trampoline->context; runtime = JS_GetRuntime(context); if (G_UNLIKELY (gjs_runtime_is_sweeping(runtime))) { 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() or dispose() vfuncs. Because it would crash the " "application, it has been blocked and the JS callback not invoked."); /* A gjs_dumpstack() would be nice here, but we can't, because that works by creating a new Error object and reading the stack property, which is the worst possible idea during a GC session. */ gjs_callback_trampoline_unref(trampoline); return; } JS_BeginRequest(context); func_obj = &trampoline->js_function.get().toObject(); JSAutoCompartment ac(context, func_obj); n_args = g_callable_info_get_n_args(trampoline->info); g_assert(n_args >= 0); n_outargs = 0; JS::AutoValueVector jsargs(context); jsargs.reserve(n_args); JS::RootedValue rval(context); JS::RootedValue rooted_function(context, trampoline->js_function); JS::RootedObject this_object(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, (GArgument *) args[array_length_pos], true)) goto out; jsargs.growBy(1); if (!gjs_value_from_explicit_array(context, jsargs[n_jsargs++], &type_info, (GArgument*) args[i], length.toInt32())) goto out; break; } case PARAM_NORMAL: jsargs.growBy(1); if (!gjs_value_from_g_argument(context, jsargs[n_jsargs++], &type_info, (GArgument *) args[i], 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 (trampoline->is_vfunc && i == 0) { g_assert(n_jsargs > 0); this_object = jsargs[0].toObjectOrNull(); jsargs.popBack(); n_jsargs--; } } if (!JS_CallFunctionValue(context, this_object, rooted_function, jsargs, &rval)) { 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, *(GArgument **)args[i])) 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, *(GArgument **)args[i])) 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 */ exit(1); } gjs_log_exception (context); /* 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 (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, 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->context = context; trampoline->info = callable_info; g_base_info_ref((GIBaseInfo*)trampoline->info); if (is_vfunc) trampoline->js_function = function; else trampoline->js_function.root(context, 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) { 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); 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); 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); 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, "TypeError", NULL, "%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)); } /* * 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; GITypeInfo return_info; GITypeTag return_tag; JS::AutoValueVector return_values(context); guint8 next_rval = 0; /* index into return_values */ GSList *iter; /* 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. */ if (completed_trampolines) { for (iter = completed_trampolines; iter; iter = iter->next) { GjsCallbackTrampoline *trampoline = (GjsCallbackTrampoline *) iter->data; gjs_callback_trampoline_unref(trampoline); } g_slist_free(completed_trampolines); completed_trampolines = NULL; } 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). * * @js_argc 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_ReportWarning(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])) 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), gjs_get_type_name(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, 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++) return_values.append(JS::UndefinedValue()); 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, -1, 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. */ struct JSClass gjs_function_class = { "GIRepositoryFunction", /* means "new GIRepositoryFunction()" works */ JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ NULL, /* convert */ function_finalize, function_call }; 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, JS::NullPtr(), &proto)) return nullptr; JS::RootedObject function(context, JS_NewObjectWithGivenProto(context, &gjs_function_class, proto)); if (function == NULL) { 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 == NULL) 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-3.6.1/gi/function.h000066400000000000000000000063221320401450000146650ustar00rootroot00000000000000/* -*- 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; JSContext *context; GICallableInfo *info; GjsMaybeOwned 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, 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-3.6.1/gi/fundamental.cpp000066400000000000000000000752241320401450000157000ustar00rootroot00000000000000/* -*- 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, 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; char *name = NULL; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GFUNDAMENTAL, "Resolve prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { g_free(name); 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; g_free(name); return true; } /* 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; g_free(name); 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); g_free(name); 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); g_free(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_CallIdTracer(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. * * We allocate 1 reserved slot; this is typically unused, but if the * fundamental 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_fundamental_instance_class = { "GFundamental_Object", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ fundamental_instance_resolve, NULL, /* convert */ fundamental_finalize, NULL, /* call */ NULL, /* hasInstance */ NULL, /* construct */ fundamental_trace }; 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 != NULL); 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 == NULL) 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 == NULL) 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, "TypeError", NULL, "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, "TypeError", NULL, "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-3.6.1/gi/fundamental.h000066400000000000000000000054741320401450000153450ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/gerror.cpp000066400000000000000000000371021320401450000146730ustar00rootroot00000000000000/* -*- 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 #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; gchar *message; 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()); 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); JS_free(context, 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, -1, 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); gchar *descr; bool retval; if (priv == NULL) return false; rec.rval().setUndefined(); retval = false; /* We follow the same pattern as standard JS errors, at the expense of hiding some useful information */ if (priv->gerror == NULL) { descr = g_strdup_printf("%s.%s", g_base_info_get_namespace(priv->info), g_base_info_get_name(priv->info)); if (!gjs_string_from_utf8(context, descr, -1, rec.rval())) goto out; } 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); if (!gjs_string_from_utf8(context, descr, -1, rec.rval())) goto out; } retval = true; out: g_free(descr); return retval; } 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. */ struct JSClass gjs_error_class = { "GLib_Error", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ NULL, /* convert */ error_finalize }; /* 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 *context, JS::HandleObject obj) { JS::RootedValue stack(context), fileName(context), lineNumber(context); if (!gjs_context_get_frame_info(context, mozilla::Some(&stack), mozilla::Some(&fileName), mozilla::Some(&lineNumber))) return; gjs_object_define_property(context, obj, GJS_STRING_STACK, stack, JSPROP_ENUMERATE); gjs_object_define_property(context, obj, GJS_STRING_FILENAME, fileName, JSPROP_ENUMERATE); gjs_object_define_property(context, obj, GJS_STRING_LINE_NUMBER, lineNumber, JSPROP_ENUMERATE); } JSObject* gjs_error_from_gerror(JSContext *context, GError *gerror, bool add_stack) { Error *priv; Error *proto_priv; GIEnumInfo *info; if (gerror == NULL) return NULL; 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 == NULL) 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); } cjs-3.6.1/gi/gerror.h000066400000000000000000000042021320401450000143330ustar00rootroot00000000000000/* -*- 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); G_END_DECLS #endif /* __GJS_ERROR_H__ */ cjs-3.6.1/gi/gjs_gi_probes.d000066400000000000000000000002111320401450000156370ustar00rootroot00000000000000provider gjs { probe object__proxy__new(void*, void*, char *, char *); probe object__proxy__finalize(void*, void*, char *, char *); }; cjs-3.6.1/gi/gjs_gi_trace.h000066400000000000000000000031671320401450000154640ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/gtype.cpp000066400000000000000000000173771320401450000145370ustar00rootroot00000000000000/* -*- 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, 0); /* 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(JSRuntime *rt, 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); if (*heap_wrapper == nullptr) iter = weak_pointer_list.erase(iter); else iter++; } } static void ensure_weak_pointer_callback(JSContext *cx) { if (!weak_pointer_callback) { JS_AddWeakPointerCallback(JS_GetRuntime(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 *context, unsigned argc, JS::Value *vp) { GJS_GET_PRIV(context, argc, vp, rec, obj, void, priv); GType gtype; gchar *strval; bool ret; gtype = GPOINTER_TO_SIZE(priv); if (gtype == 0) strval = g_strdup("[object GType prototype]"); else strval = g_strdup_printf("[object GType for '%s']", g_type_name(gtype)); ret = gjs_string_from_utf8(context, strval, -1, rec.rval()); g_free(strval); return ret; } 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), -1, 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) { 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, JS::NullPtr(), &proto)) return nullptr; heap_wrapper = new JS::Heap(); *heap_wrapper = JS_NewObjectWithGivenProto(context, &gjs_gtype_class, proto, JS::NullPtr()); 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-3.6.1/gi/gtype.h000066400000000000000000000041121320401450000141630ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/interface.cpp000066400000000000000000000205261320401450000153350ustar00rootroot00000000000000/* -*- 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 "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; char *name = NULL; GIFunctionInfo *method_info; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; } priv = priv_from_js(context, obj); if (priv == NULL) { g_free(name); 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; g_free(name); 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); g_free(name); return false; } *resolved = true; } else { *resolved = false; } g_base_info_unref((GIBaseInfo*)method_info); } else { *resolved = false; } g_free(name); return true; } struct JSClass gjs_interface_class = { "GObject_Interface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ interface_resolve, NULL, /* convert */ interface_finalize }; JSPropertySpec gjs_interface_proto_props[] = { JS_PS_END }; JSFunctionSpec gjs_interface_proto_funcs[] = { 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, JS::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() */ NULL, &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-3.6.1/gi/interface.h000066400000000000000000000036631320401450000150050ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/ns.cpp000066400000000000000000000135621320401450000140170ustar00rootroot00000000000000/* -*- 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 *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static bool ns_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { Ns *priv; char *name = NULL; GIRepository *repo; GIBaseInfo *info; bool defined; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } /* let Object.prototype resolve these */ if (strcmp(name, "valueOf") == 0 || strcmp(name, "toString") == 0) { *resolved = false; g_free(name); return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GNAMESPACE, "Resolve prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { *resolved = false; /* we are the prototype, or have the wrong class */ g_free(name); return true; } 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 */ g_free(name); 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); g_free(name); return false; } /* we defined the property in this object? */ g_base_info_unref(info); *resolved = defined; g_free(name); 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, -1, 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. */ struct JSClass gjs_ns_class = { "GIRepositoryNamespace", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ ns_resolve, NULL, /* convert */ ns_finalize }; 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, JS::NullPtr(), &proto)) return nullptr; JS::RootedObject ns(context, JS_NewObjectWithGivenProto(context, &gjs_ns_class, proto)); if (ns == NULL) 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-3.6.1/gi/ns.h000066400000000000000000000026521320401450000134620ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/object.cpp000066400000000000000000002756041320401450000146540ustar00rootroot00000000000000/* -*- 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 #include typedef struct _ConnectData ConnectData; 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 signal connections, used when tracing */ std::set signals; /* the GObjectClass wrapped by this JS Object (only used for prototypes) */ GTypeClass *klass; /* A list of all vfunc trampolines, used when tracing */ std::deque vfuncs; unsigned js_object_finalized : 1; }; struct _ConnectData { ObjectInstance *obj; GClosure *closure; }; static std::stack object_init_list; static GHashTable *class_init_properties; static bool weak_pointer_callback = false; static std::set weak_pointer_list; extern struct JSClass gjs_object_instance_class; static std::set dissociate_list; GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class) static void disassociate_js_gobject (GObject *gobj); 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 void dissociate_list_add(ObjectInstance *priv) { bool inserted; std::tie(std::ignore, inserted) = dissociate_list.insert(priv); g_assert(inserted); } static void dissociate_list_remove(ObjectInstance *priv) { size_t erased = dissociate_list.erase(priv); g_assert(erased > 0); } static ObjectInstance * get_object_qdata(GObject *gobj) { auto priv = static_cast(g_object_get_qdata(gobj, gjs_object_priv_quark())); if (priv && 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); } 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 }; if (!(g_field_info_get_flags(field) & GI_FIELD_IS_READABLE)) goto out; 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; char *name; bool ret = true; if (!gjs_get_string_id(context, id, &name)) return true; /* not resolved, but no error */ priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Get prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { /* 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 */ goto out; } if (priv->gobj == NULL) /* prototype, not an instance. */ goto out; ret = get_prop_from_g_param(context, obj, priv, name, value_p); if (!ret) goto out; if (!value_p.isUndefined()) goto out; /* Fall back to fields */ ret = get_prop_from_field(context, obj, priv, name, value_p); out: g_free(name); return ret; } static bool set_g_param_from_prop(JSContext *context, ObjectInstance *priv, const char *name, bool& was_set, JS::HandleValue value_p) { 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: return true; 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 true; } static bool check_set_field_from_prop(JSContext *cx, ObjectInstance *priv, const char *name, bool strict, JS::MutableHandleValue value_p) { if (priv->info == NULL) return true; GIFieldInfo *field = lookup_field_info(priv->info, name); if (field == NULL) return true; 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); goto out; } if (strict) { gjs_throw(cx, "Tried to set read-only field %s in strict mode", name); retval = false; goto out; } /* 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, bool strict, JS::MutableHandleValue value_p) { ObjectInstance *priv; char *name; bool ret = true; bool g_param_was_set = false; if (!gjs_get_string_id(context, id, &name)) return true; /* not resolved, but no error */ priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Set prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { /* see the comment in object_instance_get_prop() on this */ goto out; } if (priv->gobj == NULL) /* prototype, not an instance. */ goto out; ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p); if (g_param_was_set || !ret) goto out; ret = check_set_field_from_prop(context, priv, name, strict, value_p); /* 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. */ out: g_free(name); return ret; } 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, 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; } /* * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. */ static bool object_instance_resolve(JSContext *context, JS::HandleObject obj, JS::HandleId id, bool *resolved) { GIFunctionInfo *method_info; ObjectInstance *priv; char *name = NULL; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Resolve prop '%s' hook obj %p priv %p (%s.%s) gobj %p %s", name, obj.get(), 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; g_free(name); return true; } if (priv->gobj != NULL) { *resolved = false; g_free(name); return true; } /* 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); g_free(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) { g_free(name); /* 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. */ } /* 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); g_free(name); return retval; } g_free(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) { 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; } static void free_g_params(GParameter *params, int n_params) { int i; for (i = 0; i < n_params; ++i) { g_value_unset(¶ms[i].value); } } /* Set properties from args to constructor (argv[0] is supposed to be * a hash) */ 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"); free_g_params(&gparams[0], gparams.size()); return false; } JS::RootedObject props(context, &args[0].toObject()); JS::RootedId prop_id(context); JS::AutoIdArray ids(context, JS_Enumerate(context, props)); JS::RootedValue value(context); if (!ids) { gjs_throw(context, "Failed to create property iterator for object props hash"); goto free_array_and_fail; } for (ix = 0, length = ids.length(); ix < length; ix++) { char *name = NULL; 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)) { goto free_array_and_fail; } if (!gjs_get_string_id(context, prop_id, &name)) goto free_array_and_fail; 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, g_type_name(gtype)); /* fallthrough */ case SOME_ERROR_OCCURRED: g_free(name); goto free_array_and_fail; case VALUE_WAS_SET: default: break; } g_free(name); gparams.push_back(gparam); } return true; free_array_and_fail: free_g_params(&gparams[0], gparams.size()); return false; } #define DEBUG_DISPOSE 0 static void wrapped_gobj_dispose_notify(gpointer data, GObject *where_the_object_was) { weak_pointer_list.erase(static_cast(data)); #if DEBUG_DISPOSE gjs_debug(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed", where_the_object_was); #endif } static void gobj_no_longer_kept_alive_func(JS::HandleObject obj, void *data) { ObjectInstance *priv; priv = (ObjectInstance *) data; gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p will no longer be kept alive, eligible for collection", obj.get()); priv->keep_alive.reset(); dissociate_list_remove(priv); weak_pointer_list.erase(priv); } static void handle_toggle_down(GObject *gobj) { ObjectInstance *priv = get_object_qdata(gobj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify gobj %p obj %p is_last_ref true", 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()) { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Removing object from keep alive"); priv->keep_alive.switch_to_unrooted(); dissociate_list_remove(priv); } } 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 gobj %p obj %p is_last_ref false", 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, "Adding object to keep alive"); auto cx = static_cast(gjs_context_get_native_context(context)); priv->keep_alive.switch_to_rooted(cx, gobj_no_longer_kept_alive_func, priv); dissociate_list_add(priv); } } 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(); g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, NULL); 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_prepare_shutdown (JSContext *context) { /* First, get rid of anything left over on the main context */ gjs_object_clear_toggles(); /* Now, 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 simply ignoring toggle ref notifications during this process. */ for (auto iter : dissociate_list) release_native_object(iter); dissociate_list.clear(); } 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); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "obj instance constructor, obj %p priv %p", object.get(), 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); JS_EndRequest(context); return priv; } static void update_heap_wrapper_weak_pointers(JSRuntime *rt, gpointer data) { std::vector to_be_disassociated; for (auto iter = weak_pointer_list.begin(); iter != weak_pointer_list.end(); ) { ObjectInstance *priv = *iter; if (priv->keep_alive.rooted() || priv->keep_alive == nullptr || !priv->keep_alive.update_after_gc()) { iter++; } else { /* 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.) */ to_be_disassociated.push_back(priv->gobj); iter = weak_pointer_list.erase(iter); } } for (GObject *gobj : to_be_disassociated) disassociate_js_gobject(gobj); } static void ensure_weak_pointer_callback(JSContext *cx) { if (!weak_pointer_callback) { JS_AddWeakPointerCallback(JS_GetRuntime(cx), update_heap_wrapper_weak_pointers, NULL); 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->gobj = gobj; g_assert(!priv->keep_alive.rooted()); set_object_qdata(gobj, priv); ensure_weak_pointer_callback(context); weak_pointer_list.insert(priv); g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, priv); /* 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->keep_alive.root(context, object, gobj_no_longer_kept_alive_func, priv); dissociate_list_add(priv); g_object_add_toggle_ref(gobj, wrapped_gobj_toggle_notify, NULL); } static void invalidate_all_signals(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->signals.empty()) { /* This will also free cd, through the closure invalidation mechanism */ ConnectData *cd = *priv->signals.begin(); g_closure_invalidate(cd->closure); /* Erase element if not already erased */ priv->signals.erase(cd); } } static void disassociate_js_gobject(GObject *gobj) { ObjectInstance *priv = get_object_qdata(gobj); bool had_toggle_down, had_toggle_up; g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv); /* FIXME: this check fails when JS code runs after the main loop ends, * because the idle functions are not dispatched without a main loop. * The only situation I'm aware of where this happens is during the * dbus_unregister stage in GApplication. Ideally this check should be an * assertion. * https://bugzilla.gnome.org/show_bug.cgi?id=778862 */ auto& toggle_queue = ToggleQueue::get_default(); std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(gobj); if (had_toggle_down != had_toggle_up) { g_critical("JS object wrapper for GObject %p (%s) is being released " "while toggle references are still pending. This may happen " "on exit in Gio.Application.vfunc_dbus_unregister(). If you " "encounter it another situation, please report a GJS bug.", gobj, G_OBJECT_TYPE_NAME(gobj)); } invalidate_all_signals(priv); release_native_object(priv); /* Mark that a JS object once existed, but it doesn't any more */ priv->js_object_finalized = true; } 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)) { 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(), ¶ms[0]); G_GNUC_END_IGNORE_DEPRECATIONS free_g_params(¶ms[0], params.size()); ObjectInstance *other_priv = get_object_qdata(gobj); if (other_priv && other_priv->keep_alive != object) { /* 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. * */ 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); /* 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(gobj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "JSObject created with GObject %p %s", priv->gobj, g_type_name_from_instance((GTypeInstance*) 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; for (ConnectData *cd : priv->signals) gjs_closure_trace(cd->closure, tracer); for (auto vfunc : priv->vfuncs) vfunc->js_function.trace(tracer, "ObjectInstance::vfunc"); } static void signal_connection_invalidated(void *data, GClosure *closure) { auto cd = static_cast(data); cd->obj->signals.erase(cd); g_slice_free(ConnectData, cd); } static void object_instance_finalize(JSFreeOp *fop, JSObject *obj) { ObjectInstance *priv; priv = (ObjectInstance *) JS_GetPrivate(obj); gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "finalize obj %p priv %p gtype %s gobj %p", obj, priv, (priv && priv->gobj) ? g_type_name_from_instance( (GTypeInstance*) priv->gobj) : "", priv ? priv->gobj : NULL); g_assert (priv != NULL); 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_signals(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)); } release_native_object(priv); } /* We have to leak the trampolines, since the GType's vtable still refers * to them */ for (auto iter : priv->vfuncs) iter->js_function.reset(); priv->vfuncs.clear(); 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, "Removing from keep alive"); priv->keep_alive.reset(); dissociate_list_remove(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); } 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. */ gjs_define_object_class(context, in_object, NULL, gtype, &constructor); } else { if (G_UNLIKELY (!value.isObject())) return NULL; constructor = &value.toObject(); } g_assert(constructor != NULL); 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 == NULL)) 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 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; char *signal_name; GQuark signal_detail; ConnectData *connect_data; bool ret = false; 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 (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; } if (!gjs_string_to_utf8(context, argv[0], &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, g_type_name(G_OBJECT_TYPE(priv->gobj))); goto out; } closure = gjs_closure_new_for_signal(context, &argv[1].toObject(), "signal callback", signal_id); if (closure == NULL) goto out; connect_data = g_slice_new0(ConnectData); priv->signals.insert(connect_data); connect_data->obj = priv; /* This is a weak reference, and will be cleared when the closure is invalidated */ connect_data->closure = closure; g_closure_add_invalidate_notifier(closure, connect_data, signal_connection_invalidated); id = g_signal_connect_closure_by_id(priv->gobj, signal_id, signal_detail, closure, after); argv.rval().setDouble(id); ret = true; out: g_free(signal_name); return ret; } 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; char *signal_name; GValue *instance_and_args; GValue rvalue = G_VALUE_INIT; unsigned int i; bool failed; bool ret = false; 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 (argc < 1 || !argv[0].isString()) { gjs_throw(context, "emit() first arg is the signal name"); return false; } if (!gjs_string_to_utf8(context, argv[0], &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, g_type_name(G_OBJECT_TYPE(priv->gobj))); goto out; } 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, g_type_name(G_OBJECT_TYPE(priv->gobj)), signal_query.n_params, argc - 1); goto out; } 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]); } ret = !failed; out: g_free(signal_name); return ret; } 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, "object", (GIBaseInfo*)priv->info, priv->gtype, priv->gobj, rec.rval()); } struct JSClass gjs_object_instance_class = { "GObject_Object", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ object_instance_get_prop, object_instance_set_prop, NULL, /* enumerate */ object_instance_resolve, NULL, /* convert */ object_instance_finalize, NULL, NULL, NULL, object_instance_trace, }; 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) { const char *constructor_name; JS::RootedObject prototype(context), parent_proto(context); ObjectInstance *priv; const char *ns; GType parent_type; g_assert(in_object != NULL); 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 %s prototype %p class %p in object %p", constructor_name, 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 == NULL) return nullptr; priv = init_object_private(context, obj); g_object_ref_sink(gobj); associate_js_gobject(context, obj, gobj); /* see the comment in init_object_instance() for this */ g_object_unref(gobj); g_assert(priv->keep_alive == obj); } return priv->keep_alive; } GObject* gjs_g_object_from_object(JSContext *context, JS::HandleObject obj) { ObjectInstance *priv; if (obj == NULL) 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; } 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, "TypeError", NULL, "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, "TypeError", NULL, "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, gchar *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); gchar *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); g_free(name); return false; } find_vfunc_info(cx, gtype, vfunc, name, &implementor_vtable, &field_info); if (field_info != NULL) { GITypeInfo *type_info; GIBaseInfo *interface_info; GICallbackInfo *callback_info; gint offset; gpointer method_ptr; GjsCallbackTrampoline *trampoline; type_info = g_field_info_get_type(field_info); interface_info = g_type_info_get_interface(type_info); callback_info = (GICallbackInfo*)interface_info; 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, callback_info, GI_SCOPE_TYPE_NOTIFIED, true); *((ffi_closure **)method_ptr) = trampoline->closure; priv->vfuncs.push_back(trampoline); g_base_info_unref(interface_info); g_base_info_unref(type_info); g_base_info_unref(field_info); } g_base_info_unref(vfunc); g_free(name); 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); gchar *name = NULL; 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"); g_clear_pointer(&name, g_free); 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, g_type_name(gtype)); g_clear_pointer(&name, g_free); return false; } new_pspec = g_param_spec_override(name, pspec); g_clear_pointer(&name, g_free); 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) { GPtrArray *properties; GType gtype; guint i; gtype = G_TYPE_FROM_INTERFACE (g_iface); properties = (GPtrArray *) gjs_hash_table_for_gsize_lookup(class_init_properties, gtype); if (properties == NULL) return; for (i = 0; i < properties->len; i++) { GParamSpec *pspec = (GParamSpec *) properties->pdata[i]; g_param_spec_set_qdata(pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); g_object_interface_install_property(g_iface, pspec); } gjs_hash_table_for_gsize_remove(class_init_properties, gtype); } static void gjs_object_class_init(GObjectClass *klass, gpointer user_data) { GPtrArray *properties; GType gtype; guint i; 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; properties = (GPtrArray*) gjs_hash_table_for_gsize_lookup (class_init_properties, gtype); if (properties != NULL) { for (i = 0; i < properties->len; i++) { GParamSpec *pspec = (GParamSpec*) properties->pdata[i]; g_param_spec_set_qdata(pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); g_object_class_install_property (klass, i+1, pspec); } gjs_hash_table_for_gsize_remove (class_init_properties, gtype); } } 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)); 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; if (!JS_IsArrayObject(cx, interfaces)) { gjs_throw(cx, "Invalid parameter interfaces (expected Array)"); return false; } if (!JS_GetArrayLength(cx, interfaces, &n_int)) return false; if (!JS_IsArrayObject(cx, properties)) { 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) { GPtrArray *properties_native = NULL; guint32 i; if (!class_init_properties) class_init_properties = gjs_hash_table_new_for_gsize((GDestroyNotify) g_ptr_array_unref); properties_native = g_ptr_array_new_with_free_func((GDestroyNotify) g_param_spec_unref); for (i = 0; i < n_properties; i++) { JS::RootedValue prop_val(cx); if (!JS_GetElement(cx, properties, i, &prop_val)) { g_clear_pointer(&properties_native, g_ptr_array_unref); return false; } if (!prop_val.isObject()) { g_clear_pointer(&properties_native, g_ptr_array_unref); gjs_throw(cx, "Invalid parameter, expected object"); return false; } JS::RootedObject prop_obj(cx, &prop_val.toObject()); if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true)) { g_clear_pointer(&properties_native, g_ptr_array_unref); return false; } g_ptr_array_add(properties_native, g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj))); } gjs_hash_table_for_gsize_insert(class_init_properties, (gsize) gtype, g_ptr_array_ref(properties_native)); g_clear_pointer(&properties_native, g_ptr_array_unref); return true; } static bool gjs_register_interface(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); char *name = NULL; 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)) { g_clear_pointer(&name, g_free); 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)) { g_clear_pointer(&name, g_free); return false; } if (g_type_from_name(name) != G_TYPE_INVALID) { gjs_throw(cx, "Type name %s is already registered", name); g_clear_pointer(&name, g_free); return false; } interface_type = g_type_register_static(G_TYPE_INTERFACE, name, &type_info, (GTypeFlags) 0); g_clear_pointer(&name, g_free); 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 == NULL) 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 bool gjs_register_type(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); gchar *name; GType instance_type, parent_type; GTypeQuery query; ObjectInstance *parent_priv; GTypeInfo type_info = { 0, /* class_size */ (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (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); 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_free(name); 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)), constructor(cx); gjs_define_object_class(cx, module, NULL, instance_type, &constructor); 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; char *signal_name_tmp = NULL; 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_tmp)) return false; std::unique_ptr signal_name(signal_name_tmp, g_free); 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.get(), 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; } cjs-3.6.1/gi/object.h000066400000000000000000000057711320401450000143150ustar00rootroot00000000000000/* -*- 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); 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 (JSContext *context); void gjs_object_clear_toggles(void); 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); G_END_DECLS #endif /* __GJS_OBJECT_H__ */ cjs-3.6.1/gi/param.cpp000066400000000000000000000230761320401450000145000ustar00rootroot00000000000000/* -*- 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; char *name = NULL; bool ret = false; if (!gjs_get_string_id(context, id, &name)) return true; /* not resolved, but no error */ priv = priv_from_js(context, obj); if (priv != NULL) { /* instance, not prototype */ *resolved = false; g_free(name); return true; } 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: g_free(name); 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. */ struct JSClass gjs_param_class = { "GObject_ParamSpec", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ param_resolve, NULL, /* convert */ param_finalize }; 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 != NULL); 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, JS::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 == NULL) 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, "TypeError", NULL, "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, "TypeError", NULL, "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-3.6.1/gi/param.h000066400000000000000000000036741320401450000141470ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/proxyutils.cpp000066400000000000000000000052721320401450000156400ustar00rootroot00000000000000/* -*- 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, -1, rval)) goto out; ret = true; out: g_string_free (buf, true); return ret; } cjs-3.6.1/gi/proxyutils.h000066400000000000000000000033541320401450000153040ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/repo.cpp000066400000000000000000000577351320401450000143560ustar00rootroot00000000000000/* -*- 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/jsapi-private.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, char **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) { *version = NULL; 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; char *version; JSAutoRequest ar(context); 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_ReportWarning(context, warn_text); } 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:"none", error->message); g_error_free(error); g_free(version); return false; } g_free(version); /* 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; char *name = NULL; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } /* let Object.prototype resolve these */ if (strcmp(name, "valueOf") == 0 || strcmp(name, "toString") == 0) { *resolved = false; g_free(name); return true; } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GREPO, "Resolve prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { /* we are the prototype, or have the wrong class */ *resolved = false; g_free(name); return true; } if (!resolve_namespace_object(context, obj, id, name)) { g_free(name); return false; } *resolved = true; g_free(name); 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. */ struct JSClass gjs_repo_class = { "GIRepository", /* means "new GIRepository()" works */ JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ repo_resolve, NULL, /* convert */ repo_finalize }; 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, JS::NullPtr(), &proto)) return nullptr; JS::RootedObject repo(context, JS_NewObjectWithGivenProto(context, &gjs_repo_class, proto)); if (repo == NULL) { gjs_throw(context, "No memory to create repo object"); return NULL; } 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); /* 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); 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 ignored(context); gjs_define_object_class(context, in_object, (GIObjectInfo *) info, gtype, &ignored); } 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_InternString(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 == NULL)) 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-3.6.1/gi/repo.h000066400000000000000000000051501320401450000140030ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/toggle.cpp000066400000000000000000000112501320401450000146500ustar00rootroot00000000000000/* -*- 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) { 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); 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(); } if (item.needs_unref) g_object_unref(item.gobj); return true; } void ToggleQueue::enqueue(GObject *gobj, ToggleQueue::Direction direction, ToggleQueue::Handler handler) { 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) { g_object_ref(gobj); item.needs_unref = true; } /* 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-3.6.1/gi/toggle.h000066400000000000000000000061041320401450000143170ustar00rootroot00000000000000/* -*- 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 /* 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; unsigned m_idle_id; Handler m_toggle_handler; 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); /* 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-3.6.1/gi/union.cpp000066400000000000000000000366471320401450000145400ustar00rootroot00000000000000/* -*- 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; char *name = NULL; if (!gjs_get_string_id(context, id, &name)) { *resolved = false; return true; /* not resolved, but no error */ } priv = priv_from_js(context, obj); gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook obj %p priv %p", name, obj.get(), priv); if (priv == NULL) { g_free(name); 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; g_free(name); return true; } /* 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); g_free(name); return false; } *resolved = true; /* we defined the prop in object_proto */ } else { *resolved = false; } g_base_info_unref( (GIBaseInfo*) method_info); } else { *resolved = false; } g_free(name); 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. */ struct JSClass gjs_union_class = { "GObject_Union", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ union_resolve, NULL, /* convert */ union_finalize }; 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, JS::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 == NULL) 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, "TypeError", NULL, "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, "TypeError", NULL, "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, "TypeError", NULL, "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-3.6.1/gi/union.h000066400000000000000000000042571320401450000141750ustar00rootroot00000000000000/* -*- 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-3.6.1/gi/value.cpp000066400000000000000000001070011320401450000145030ustar00rootroot00000000000000/* -*- 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/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; JSRuntime *runtime; 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); runtime = JS_GetRuntime(context); if (G_UNLIKELY (gjs_runtime_is_sweeping(runtime))) { 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() or dispose() 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); } /* A gjs_dumpstack() would be nice here, but we can't, because that works by creating a new Error object and reading the stack property, which is the worst possible idea during a GC session. */ 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); argv.reserve(n_param_values); /* May end up being less */ 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; } argv.append(argv_to_append); } 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, argv, &rval); 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 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()) { gchar *utf8_string; if (!gjs_string_to_utf8(context, value, &utf8_string)) return false; g_value_take_string(gvalue, utf8_string); } else { gjs_throw(context, "Wrong type %s; string expected", gjs_get_type_name(value)); return false; } } 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 { gjs_throw(context, "Wrong type %s; char expected", gjs_get_type_name(value)); return false; } } 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 { gjs_throw(context, "Wrong type %s; unsigned char expected", gjs_get_type_name(value)); return false; } } else if (gtype == G_TYPE_INT) { gint32 i; if (JS::ToInt32(context, value, &i)) { g_value_set_int(gvalue, i); } else { gjs_throw(context, "Wrong type %s; integer expected", gjs_get_type_name(value)); return false; } } else if (gtype == G_TYPE_DOUBLE) { gdouble d; if (JS::ToNumber(context, value, &d)) { g_value_set_double(gvalue, d); } else { gjs_throw(context, "Wrong type %s; double expected", gjs_get_type_name(value)); return false; } } else if (gtype == G_TYPE_FLOAT) { gdouble d; if (JS::ToNumber(context, value, &d)) { g_value_set_float(gvalue, d); } else { gjs_throw(context, "Wrong type %s; float expected", gjs_get_type_name(value)); return false; } } else if (gtype == G_TYPE_UINT) { guint32 i; if (JS::ToUint32(context, value, &i)) { g_value_set_uint(gvalue, i); } else { gjs_throw(context, "Wrong type %s; unsigned integer expected", gjs_get_type_name(value)); return false; } } 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 { gjs_throw(context, "Wrong type %s; object %s expected", gjs_get_type_name(value), g_type_name(gtype)); return false; } 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); gjs_throw(context, "Wrong type %s; strv expected", gjs_get_type_name(value)); return false; } 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 { gjs_throw(context, "Wrong type %s; strv expected", gjs_get_type_name(value)); return false; } } } 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 { gjs_throw(context, "Wrong type %s; boxed type %s expected", gjs_get_type_name(value), g_type_name(gtype)); return false; } 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 { gjs_throw(context, "Wrong type %s; boxed type %s expected", gjs_get_type_name(value), g_type_name(gtype)); return false; } 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 { gjs_throw(context, "Wrong type %s; enum %s expected", gjs_get_type_name(value), g_type_name(gtype)); return false; } } 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 { gjs_throw(context, "Wrong type %s; flags %s expected", gjs_get_type_name(value), g_type_name(gtype)); return false; } } 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 { gjs_throw(context, "Wrong type %s; param type %s expected", gjs_get_type_name(value), g_type_name(gtype)); return false; } g_value_set_param(gvalue, (GParamSpec*) gparam); } else if (g_type_is_a(gtype, G_TYPE_GTYPE)) { GType type; if (!value.isObject()) { gjs_throw(context, "Wrong type %s; expect a GType object", gjs_get_type_name(value)); return false; } 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 { gjs_throw(context, "Wrong type %s; integer expected", gjs_get_type_name(value)); return false; } } 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, -1, 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; GIBaseInfo *obj; GISignalInfo *signal_info; GITypeInfo type_info; obj = g_irepository_find_by_gtype(NULL, signal_query->itype); if (!obj) { gjs_throw(context, "Signal argument with GType %s isn't introspectable", g_type_name(signal_query->itype)); return false; } signal_info = g_object_info_find_signal((GIObjectInfo*)obj, signal_query->signal_name); if (!signal_info) { gjs_throw(context, "Unknown signal."); g_base_info_unref((GIBaseInfo*)obj); 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); g_base_info_unref((GIBaseInfo*)obj); 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-3.6.1/gi/value.h000066400000000000000000000045421320401450000141560ustar00rootroot00000000000000/* -*- 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-3.6.1/git.mk000066400000000000000000000265551320401450000134160ustar00rootroot00000000000000# 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-3.6.1/gjs-modules-srcs.mk000066400000000000000000000014551320401450000160240ustar00rootroot00000000000000module_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-3.6.1/gjs-srcs.mk000066400000000000000000000042131320401450000143510ustar00rootroot00000000000000gjs_public_headers = \ cjs/context.h \ cjs/coverage.h \ cjs/gjs.h \ cjs/macros.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-internal.h \ cjs/coverage.cpp \ cjs/importer.cpp \ cjs/importer.h \ cjs/jsapi-class.h \ cjs/jsapi-constructor-proxy.cpp \ cjs/jsapi-constructor-proxy.h \ cjs/jsapi-dynamic-class.cpp \ cjs/jsapi-private.cpp \ cjs/jsapi-private.h \ 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/native.cpp \ cjs/native.h \ cjs/runtime.cpp \ cjs/runtime.h \ cjs/stack.cpp \ modules/modules.cpp \ modules/modules.h \ util/error.cpp \ util/glib.cpp \ util/glib.h \ util/hash-x32.cpp \ util/hash-x32.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) cjs-3.6.1/gjs.doap000066400000000000000000000035011320401450000137140ustar00rootroot00000000000000 gjs gjs GNOME JavaScript/Spidermonkey bindings GNOME JavaScript/Spidermonkey bindings C Philip Chimento ptomato pchimento Colin Walters walters Giovanni Campagna gcampagna cjs-3.6.1/installed-tests/000077500000000000000000000000001320401450000154045ustar00rootroot00000000000000cjs-3.6.1/installed-tests/extra/000077500000000000000000000000001320401450000165275ustar00rootroot00000000000000cjs-3.6.1/installed-tests/extra/gjs.supp000066400000000000000000000042411320401450000202240ustar00rootroot00000000000000{ g_type_init_with_debug_flags calloc Memcheck:Leak fun:calloc ... fun:g_type_init_with_debug_flags ... } { g_type_add_interface_static malloc Memcheck:Leak fun:malloc ... fun:g_type_add_interface_static ... } { g_type_add_interface_dynamic malloc Memcheck:Leak fun:malloc ... fun:g_type_add_interface_dynamic ... } { g_type_class_ref malloc Memcheck:Leak fun:malloc ... fun:g_type_class_ref ... } { g_type_register_dynamic malloc Memcheck:Leak fun:malloc ... fun:g_type_register_dynamic ... } { g_type_init_with_debug_flags malloc Memcheck:Leak fun:malloc ... fun:g_type_init_with_debug_flags ... } { g_type_init_with_debug_flags realloc Memcheck:Leak fun:realloc ... fun:g_type_init_with_debug_flags ... } { g_test_add_vtable malloc Memcheck:Leak fun:malloc ... fun:g_test_add_vtable ... } { g_test_init Memcheck:Leak fun:malloc ... fun:g_test_init ... } { g_type_register_static malloc Memcheck:Leak fun:malloc ... fun:g_type_register_static ... } { g_type_register_static realloc Memcheck:Leak fun:realloc ... fun:g_type_register_static ... } { g_type_register_fundamental never freed Memcheck:Leak fun:malloc ... fun:g_type_register_fundamental ... } { g_type_class_ref never finalized Memcheck:Leak fun:calloc fun:g_malloc0 fun:g_type_class_ref ... } { DBusGValue qdata Memcheck:Leak fun:realloc fun:g_realloc fun:g_type_set_qdata fun:_dbus_g_value_types_init ... } { gettext conditional jump Memcheck:Cond fun:__GI___strcasecmp_l fun:__gconv_open fun:_nl_find_msg fun:__dcigettext ... } { gettext uninitialized value Memcheck:Value8 fun:__GI___strcasecmp_l fun:__gconv_open fun:_nl_find_msg fun:__dcigettext ... } { font config invalid reads Memcheck:Addr4 ... fun:FcConfigParseAndLoad ... } { dynamic loader conditional jump Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main ... } cjs-3.6.1/installed-tests/extra/unittest.gdb000066400000000000000000000000461320401450000210640ustar00rootroot00000000000000run if ($_exitcode == 0) quit end cjs-3.6.1/installed-tests/js/000077500000000000000000000000001320401450000160205ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/complex.ui000066400000000000000000000017341320401450000200330ustar00rootroot00000000000000 cjs-3.6.1/installed-tests/js/jasmine.js000066400000000000000000002766651320401450000200320ustar00rootroot00000000000000/* 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-3.6.1/installed-tests/js/jsunit.gresources.xml000066400000000000000000000013731320401450000222420ustar00rootroot00000000000000 complex.ui jasmine.js minijasmine.js modules/alwaysThrows.js modules/foobar.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-3.6.1/installed-tests/js/minijasmine.js000066400000000000000000000071041320401450000206630ustar00rootroot00000000000000#!/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 const TapReporter = new Lang.Class({ Name: 'TapReporter', _init: function () { this._failedSuites = []; this._specCount = 0; }, jasmineStarted: function (info) { print('1..' + info.totalSpecsDefined); }, jasmineDone: function () { 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: function (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: function () { this._specCount++; }, specDone: function (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-3.6.1/installed-tests/js/modules/000077500000000000000000000000001320401450000174705ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/modules/alwaysThrows.js000066400000000000000000000001411320401450000225310ustar00rootroot00000000000000// line 0 // line 1 // line 2 throw new Error("This is an error that always happens on line 3"); cjs-3.6.1/installed-tests/js/modules/foobar.js000066400000000000000000000001431320401450000212740ustar00rootroot00000000000000// simple test module (used by testImporter.js) var foo = "This is foo"; var bar = "This is bar"; cjs-3.6.1/installed-tests/js/modules/modunicode.js000066400000000000000000000001021320401450000221450ustar00rootroot00000000000000// This file is written in UTF-8. const uval = "const ♥ utf8"; cjs-3.6.1/installed-tests/js/modules/mutualImport/000077500000000000000000000000001320401450000221725ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/modules/mutualImport/a.js000066400000000000000000000003001320401450000227410ustar00rootroot00000000000000const B = imports.mutualImport.b; let count = 0; function incrementCount() { count++; } function getCount() { return count; } function getCountViaB() { return B.getCount(); } cjs-3.6.1/installed-tests/js/modules/mutualImport/b.js000066400000000000000000000001251320401450000227470ustar00rootroot00000000000000const A = imports.mutualImport.a; function getCount() { return A.getCount(); } cjs-3.6.1/installed-tests/js/modules/overrides/000077500000000000000000000000001320401450000214725ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/modules/overrides/GIMarshallingTests.js000066400000000000000000000001101320401450000255240ustar00rootroot00000000000000// Sabotage the import of imports.gi.GIMarshallingTests! throw '💩'; cjs-3.6.1/installed-tests/js/modules/overrides/Gio.js000066400000000000000000000000771320401450000225520ustar00rootroot00000000000000// Sabotage the import of imports.gi.Gio! var _init = '💩'; cjs-3.6.1/installed-tests/js/modules/overrides/Regress.js000066400000000000000000000001261320401450000234410ustar00rootroot00000000000000// Sabotage the import of imports.gi.Regress! function _init() { throw '💩'; } cjs-3.6.1/installed-tests/js/modules/overrides/WarnLib.js000066400000000000000000000001101320401450000233560ustar00rootroot00000000000000// Sabotage the import of imports.gi.WarnLib! k$^s^%$#^*($%jdghdsfjkgd cjs-3.6.1/installed-tests/js/modules/subA/000077500000000000000000000000001320401450000203625ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/modules/subA/subB/000077500000000000000000000000001320401450000212555ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/modules/subA/subB/__init__.js000066400000000000000000000004421320401450000233520ustar00rootroot00000000000000function testImporterFunction() { return "__init__ function tested"; } function ImporterClass() { this._init(); } ImporterClass.prototype = { _init : function() { this._a = "__init__ class tested"; }, testMethod : function() { return this._a; } } cjs-3.6.1/installed-tests/js/modules/subA/subB/baz.js000066400000000000000000000000001320401450000223550ustar00rootroot00000000000000cjs-3.6.1/installed-tests/js/modules/subA/subB/foobar.js000066400000000000000000000001431320401450000230610ustar00rootroot00000000000000// simple test module (used by testImporter.js) var foo = "This is foo"; var bar = "This is bar"; cjs-3.6.1/installed-tests/js/testByteArray.js000066400000000000000000000074531320401450000211710ustar00rootroot00000000000000const 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-3.6.1/installed-tests/js/testCairo.js000066400000000000000000000165011320401450000203160ustar00rootroot00000000000000imports.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(_ts(cr.getTarget())).toEqual('Surface'); }); }); 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'); }); }); }); cjs-3.6.1/installed-tests/js/testClass.js000066400000000000000000000122371320401450000203300ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- const Lang = imports.lang; 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('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('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]); }); }); cjs-3.6.1/installed-tests/js/testCoverage.js000066400000000000000000001100541320401450000210120ustar00rootroot00000000000000const Coverage = imports.coverage; describe('Coverage.expressionLinesForAST', function () { let testTable = { 'works with no trailing newline': [ "let x;\n" + "let y;", [1, 2], ], 'finds lines on both sides of an assignment expression': [ "var x;\n" + "x = (function() {\n" + " return 10;\n" + "})();\n", [1, 2, 3], ], 'finds lines inside functions': [ "function f(a, b) {\n" + " let x = a;\n" + " let y = b;\n" + " return x + y;\n" + "}\n" + "\n" + "var z = f(1, 2);\n", [2, 3, 4, 7], ], 'finds lines inside anonymous functions': [ "var z = (function f(a, b) {\n" + " let x = a;\n" + " let y = b;\n" + " return x + y;\n" + " })();\n", [1, 2, 3, 4], ], 'finds lines inside body of function property': [ "var o = {\n" + " foo: function() {\n" + " let x = a;\n" + " }\n" + "};\n", [1, 2, 3], ], 'finds lines inside arguments of function property': [ "function f(a) {\n" + "}\n" + "f({\n" + " foo: function() {\n" + " let x = a;\n" + " }\n" + "});\n", [1, 3, 4, 5], ], 'finds lines inside multiline function arguments': [ "function f(a, b, c) {\n" + "}\n" + "f(1,\n" + " 2,\n" + " 3);\n", [1, 3, 4, 5], ], 'finds lines inside function argument that is an object': [ "function f(o) {\n" + "}\n" + "let obj = {\n" + " Name: new f({ a: 1,\n" + " b: 2,\n" + " c: 3\n" + " })\n" + "}\n", [1, 3, 4, 5, 6], ], 'finds lines inside a while loop': [ "var a = 0;\n" + "while (a < 1) {\n" + " let x = 0;\n" + " let y = 1;\n" + " a++;" + "\n" + "}\n", [1, 2, 3, 4, 5], ], 'finds lines inside try, catch, and finally': [ "var a = 0;\n" + "try {\n" + " a++;\n" + "} catch (e) {\n" + " a++;\n" + "} finally {\n" + " a++;\n" + "}\n", [1, 2, 3, 4, 5, 7], ], 'finds lines inside case statements': [ "var a = 0;\n" + "switch (a) {\n" + "case 1:\n" + " a++;\n" + " break;\n" + "case 2:\n" + " a++;\n" + " break;\n" + "}\n", [1, 2, 4, 5, 7, 8], ], 'finds lines inside case statements with character cases': [ "var a = 'a';\n" + "switch (a) {\n" + "case 'a':\n" + " a++;\n" + " break;\n" + "case 'b':\n" + " a++;\n" + " break;\n" + "}\n", [1, 2, 4, 5, 7, 8], ], 'finds lines inside a for loop': [ "for (let i = 0; i < 1; i++) {\n" + " let x = 0;\n" + " let y = 1;\n" + "\n" + "}\n", [1, 2, 3], ], 'finds lines inside if-statement branches': [ "if (1 > 0) {\n" + " let i = 0;\n" + "} else {\n" + " let j = 1;\n" + "}\n", [1, 2, 4], ], 'finds all lines of multiline if-conditions': [ "if (1 > 0 &&\n" + " 2 > 0 &&\n" + " 3 > 0) {\n" + " let a = 3;\n" + "}\n", [1, 2, 3, 4], ], 'finds lines for object property literals': [ "var a = {\n" + " Name: 'foo',\n" + " Ex: 'bar'\n" + "};\n", [1, 2, 3], ], 'finds lines for function-valued object properties': [ "var a = {\n" + " Name: function() {},\n" + "};\n", [1, 2], ], 'finds lines inside object-valued object properties': [ "var a = {\n" + " Name: {},\n" + "};\n", [1, 2], ], 'finds lines inside array-valued object properties': [ "var a = {\n" + " Name: [],\n" + "};\n", [1, 2], ], 'finds lines inside object-valued argument to return statement': [ "function f() {\n" + " return {};\n" + "}\n", [2], ], 'finds lines inside object-valued argument to throw statement': [ "function f() {\n" + " throw {\n" + " a: 1,\n" + " b: 2\n" + " }\n" + "}\n", [2, 3, 4], ], }; Object.keys(testTable).forEach(testcase => { it(testcase, function () { const ast = Reflect.parse(testTable[testcase][0]); let foundLines = Coverage.expressionLinesForAST(ast); expect(foundLines).toEqual(testTable[testcase][1]); }); }); }); describe('Coverage.functionsForAST', function () { let testTable = { 'works with no trailing newline': [ "function f1() {}\n" + "function f2() {}", [ { key: "f1:1:0", line: 1, n_params: 0 }, { key: "f2:2:0", line: 2, n_params: 0 }, ], ], 'finds functions': [ "function f1() {}\n" + "function f2() {}\n" + "function f3() {}\n", [ { key: "f1:1:0", line: 1, n_params: 0 }, { key: "f2:2:0", line: 2, n_params: 0 }, { key: "f3:3:0", line: 3, n_params: 0 } ], ], 'finds nested functions': [ "function f1() {\n" + " let f2 = function() {\n" + " let f3 = function() {\n" + " }\n" + " }\n" + "}\n", [ { key: "f1:1:0", line: 1, n_params: 0 }, { key: "(anonymous):2:0", line: 2, n_params: 0 }, { key: "(anonymous):3:0", line: 3, n_params: 0 } ], ], /* Note the lack of newlines. This is all on one line */ 'finds functions on the same line but with different arguments': [ "function f1() {" + " return (function(a) {" + " return function(a, b) {}" + " });" + "}", [ { key: "f1:1:0", line: 1, n_params: 0 }, { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:2", line: 1, n_params: 2 } ], ], 'finds functions inside an array expression': [ "let a = [function() {}];\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 }, ], ], 'finds functions inside an arrow expression': [ "(a) => (function() {})();\n", [ { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside a sequence': [ "(function(a) {})()," + "(function(a, b) {})();\n", [ { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:2", line: 1, n_params: 2 }, ], ], 'finds functions inside a unary expression': [ "let a = (function() {}())++;\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 }, ], ], 'finds functions inside a binary expression': [ "let a = function(a) {}() +" + " function(a, b) {}();\n", [ { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:2", line: 1, n_params: 2 } ], ], 'finds functions inside an assignment expression': [ "let a = function() {}();\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside a reflexive assignment expression': [ "let a;\n" + "a += function() {}();\n", [ { key: "(anonymous):2:0", line: 2, n_params: 0 } ], ], 'finds functions inside if-statement conditions': [ "if (function(a) {}(a) >" + " function(a, b) {}(a, b)) {}\n", [ { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:2", line: 1, n_params: 2 } ], ], 'finds functions inside while-statement conditions': [ "while (function(a) {}(a) >" + " function(a, b) {}(a, b)) {};\n", [ { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:2", line: 1, n_params: 2 } ], ], 'finds functions inside for-statement initializer': [ "for (function() {}; ;) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], /* SpiderMonkey parses for (let i = ; ; ) as though * they were let i = { for (; ) } so test the * LetStatement initializer case too */ 'finds functions inside let-statement in for-statement initializer': [ "for (let i = function() {}; ;) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside var-statement inside for-statement initializer': [ "for (var i = function() {}; ;) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside for-statement condition': [ "for (; function() {}();) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside for-statement increment': [ "for (; ;function() {}()) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside for-in-statement': [ "for (let x in function() {}()) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside for-each statement': [ "for each (x in function() {}()) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions inside for-of statement': [ "for (x of (function() {}())) {}\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds function literals used as an object': [ "f = function() {}.bind();\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds function literals used as an object in a dynamic property expression': [ "f = function() {}['bind']();\n", [ { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions on either side of a logical expression': [ "let f = function(a) {} ||" + " function(a, b) {};\n", [ { key: "(anonymous):1:1", line: 1, n_params: 1 }, { key: "(anonymous):1:2", line: 1, n_params: 2 } ], ], 'finds functions on either side of a conditional expression': [ "let a\n" + "let f = a ? function(a) {}() :" + " function(a, b) {}();\n", [ { key: "(anonymous):2:1", line: 2, n_params: 1 }, { key: "(anonymous):2:2", line: 2, n_params: 2 } ], ], 'finds functions as the argument of a yield statement': [ "function a() { yield function (){} };\n", [ { key: "a:1:0", line: 1, n_params: 0 }, { key: "(anonymous):1:0", line: 1, n_params: 0 } ], ], 'finds functions in an array comprehension body': [ "let a = new Array(1);\n" + "let b = [function() {} for (i of a)];\n", [ { key: "(anonymous):2:0", line: 2, n_params: 0 } ], ], 'finds functions in an array comprehension block': [ "let a = new Array(1);\n" + "let b = [i for (i of function() {})];\n", [ { key: "(anonymous):2:0", line: 2, n_params: 0 } ], ], 'finds functions in an array comprehension filter': [ "let a = new Array(1);\n" + "let b = [i for (i of a)" + "if (function() {}())];\n", [ { key: "(anonymous):2:0", line: 2, n_params: 0 } ], ], }; Object.keys(testTable).forEach(testcase => { it(testcase, function () { const ast = Reflect.parse(testTable[testcase][0]); let foundFuncs = Coverage.functionsForAST(ast); expect(foundFuncs).toEqual(testTable[testcase][1]); }); }); }); describe('Coverage.branchesForAST', function () { let testTable = { 'works with no trailing newline': [ "if (1) { let a = 1; }", [ { point: 1, exits: [1] }, ], ], 'finds both branch exits for a simple branch': [ "if (1) {\n" + " let a = 1;\n" + "} else {\n" + " let b = 2;\n" + "}\n", [ { point: 1, exits: [2, 4] } ], ], 'finds a single exit for a branch with one consequent': [ "if (1) {\n" + " let a = 1.0;\n" + "}\n", [ { point: 1, exits: [2] } ], ], 'finds multiple exits for nested if-else branches': [ "if (1) {\n" + " let a = 1.0;\n" + "} else if (2) {\n" + " let b = 2.0;\n" + "} else if (3) {\n" + " let c = 3.0;\n" + "} else {\n" + " let d = 4.0;\n" + "}\n", [ // the 'else if' line is actually an exit for the first branch { point: 1, exits: [2, 3] }, { point: 3, exits: [4, 5] }, // 'else' by itself is not executable, it is the block it // contains which is { point: 5, exits: [6, 8] } ], ], 'finds a simple two-exit branch without blocks': [ "let a, b;\n" + "if (1)\n" + " a = 1.0\n" + "else\n" + " b = 2.0\n" + "\n", [ { point: 2, exits: [3, 5] } ], ], 'does not find a branch if the consequent was empty': [ "let a, b;\n" + "if (1) {}\n", [], ], 'finds a single exit if only the alternate exit was defined': [ "let a, b;\n" + "if (1) {}\n" + "else\n" + " a++;\n", [ { point: 2, exits: [4] } ], ], 'finds an implicit branch for while statement': [ "while (1) {\n" + " let a = 1;\n" + "}\n" + "let b = 2;", [ { point: 1, exits: [2] } ], ], 'finds an implicit branch for a do-while statement': [ "do {\n" + " let a = 1;\n" + "} while (1)\n" + "let b = 2;", [ // For do-while loops the branch-point is at the 'do' condition // and not the 'while' { point: 1, exits: [2] } ], ], 'finds all exits for case statements': [ "let a = 1;\n" + "switch (1) {\n" + "case '1':\n" + " a++;\n" + " break;\n" + "case '2':\n" + " a++\n" + " break;\n" + "default:\n" + " a++\n" + " break;\n" + "}\n", [ /* There are three potential exits here */ { point: 2, exits: [4, 7, 10] } ], ], 'finds all exits for case statements with fallthrough': [ "let a = 1;\n" + "switch (1) {\n" + "case '1':\n" + "case 'a':\n" + "case 'b':\n" + " a++;\n" + " break;\n" + "case '2':\n" + " a++\n" + " break;\n" + "default:\n" + " a++\n" + " break;\n" + "}\n", [ /* There are three potential exits here */ { point: 2, exits: [6, 9, 12] } ], ], 'finds no exits for case statements with only no-ops': [ "let a = 1;\n" + "switch (1) {\n" + "case '1':\n" + "case '2':\n" + "default:\n" + "}\n", [], ], }; Object.keys(testTable).forEach(testcase => { it(testcase, function () { const ast = Reflect.parse(testTable[testcase][0]); let foundBranchExits = Coverage.branchesForAST(ast); expect(foundBranchExits).toEqual(testTable[testcase][1]); }); }); }); describe('Coverage', function () { it('gets the number of lines in the script', function () { let script = "\n\n"; let number = Coverage._getNumberOfLinesForScript(script); expect(number).toEqual(3); }); it('turns zero expression lines into counters', function () { let expressionLines = []; let nLines = 1; let counters = Coverage._expressionLinesToCounters(expressionLines, nLines); expect(counters).toEqual([undefined, undefined]); }); it('turns a single expression line into counters', function () { let expressionLines = [1, 2]; let nLines = 4; let counters = Coverage._expressionLinesToCounters(expressionLines, nLines); expect(counters).toEqual([undefined, 0, 0, undefined, undefined]); }); it('returns empty array for no branches', function () { let counters = Coverage._branchesToBranchCounters([], 1); expect(counters).toEqual([undefined, undefined]); }); describe('branch counters', function () { const MockFoundBranches = [ { point: 5, exits: [6, 8] }, { point: 1, exits: [2, 4] } ]; const MockNLines = 9; let counters; beforeEach(function () { counters = Coverage._branchesToBranchCounters(MockFoundBranches, MockNLines); }); it('gets same number of counters as number of lines plus one', function () { expect(counters.length).toEqual(MockNLines + 1); }); it('branches on lines for array indices', function () { expect(counters[1]).toBeDefined(); expect(counters[5]).toBeDefined(); }); it('sets exits for branch', function () { expect(counters[1].exits).toEqual([ { line: 2, hitCount: 0 }, { line: 4, hitCount: 0 }, ]); }); it('sets last exit to highest exit start line', function () { expect(counters[1].lastExit).toEqual(4); }); it('always has hit initially false', function () { expect(counters[1].hit).toBeFalsy(); }); describe('branch tracker', function () { let branchTracker; beforeEach(function () { branchTracker = new Coverage._BranchTracker(counters); }); it('sets branch to hit on point execution', function () { branchTracker.incrementBranchCounters(1); expect(counters[1].hit).toBeTruthy(); }); it('sets exit to hit on execution', function () { branchTracker.incrementBranchCounters(1); branchTracker.incrementBranchCounters(2); expect(counters[1].exits[0].hitCount).toEqual(1); }); it('finds next branch', function () { branchTracker.incrementBranchCounters(1); branchTracker.incrementBranchCounters(2); branchTracker.incrementBranchCounters(5); expect(counters[5].hit).toBeTruthy(); }); }); }); it('function key from function with name matches schema', function () { let functionKeyForFunctionName = Coverage._getFunctionKeyFromReflectedFunction({ id: { name: 'f' }, loc: { start: { line: 1 } }, params: ['a', 'b'] }); expect(functionKeyForFunctionName).toEqual('f:1:2'); }); it('function key from function without name is anonymous', function () { let functionKeyForAnonymousFunction = Coverage._getFunctionKeyFromReflectedFunction({ id: null, loc: { start: { line: 2 } }, params: ['a', 'b', 'c'] }); expect(functionKeyForAnonymousFunction).toEqual('(anonymous):2:3'); }); it('returns a function counter map for function keys', function () { let ast = { body: [{ type: 'FunctionDeclaration', id: { name: 'name' }, loc: { start: { line: 1 } }, params: [], body: { type: 'BlockStatement', body: [] } }] }; let detectedFunctions = Coverage.functionsForAST(ast); let functionCounters = Coverage._functionsToFunctionCounters('script', detectedFunctions); expect(functionCounters.name['1']['0'].hitCount).toEqual(0); }); it('reports an error when two indistinguishable functions are present', function () { spyOn(window, 'log'); let ast = { body: [{ type: 'FunctionDeclaration', id: { name: '(anonymous)' }, loc: { start: { line: 1 } }, params: [], body: { type: 'BlockStatement', body: [] } }, { type: 'FunctionDeclaration', id: { name: '(anonymous)' }, loc: { start: { line: 1 } }, params: [], body: { type: 'BlockStatement', body: [] } }] }; let detectedFunctions = Coverage.functionsForAST(ast); Coverage._functionsToFunctionCounters('script', detectedFunctions); expect(window.log).toHaveBeenCalledWith('script:1 Function ' + 'identified as (anonymous):1:0 already seen in this file. ' + 'Function coverage will be incomplete.'); }); it('populates a known functions array', function () { let functions = [ { line: 1 }, { line: 2 } ]; let knownFunctionsArray = Coverage._populateKnownFunctions(functions, 4); expect(knownFunctionsArray) .toEqual([undefined, true, true, undefined, undefined]); }); it('converts function counters to an array', function () { let functionsMap = { '(anonymous)': { '2': { '0': { hitCount: 1 }, }, }, 'name': { '1': { '0': { hitCount: 0 }, }, } }; let expectedFunctionCountersArray = [ jasmine.objectContaining({ name: '(anonymous):2:0', hitCount: 1 }), jasmine.objectContaining({ name: 'name:1:0', hitCount: 0 }) ]; let convertedFunctionCounters = Coverage._convertFunctionCountersToArray(functionsMap); expect(convertedFunctionCounters).toEqual(expectedFunctionCountersArray); }); }); describe('Coverage.incrementFunctionCounters', function () { it('increments for function on same execution start line', function () { let functionCounters = Coverage._functionsToFunctionCounters('script', [ { key: 'f:1:0', line: 1, n_params: 0 } ]); Coverage._incrementFunctionCounters(functionCounters, null, 'f', 1, 0); expect(functionCounters.f['1']['0'].hitCount).toEqual(1); }); it('can disambiguate two functions with the same name', function () { let functionCounters = Coverage._functionsToFunctionCounters('script', [ { key: '(anonymous):1:0', line: 1, n_params: 0 }, { key: '(anonymous):2:0', line: 2, n_params: 0 } ]); Coverage._incrementFunctionCounters(functionCounters, null, '(anonymous)', 1, 0); Coverage._incrementFunctionCounters(functionCounters, null, '(anonymous)', 2, 0); expect(functionCounters['(anonymous)']['1']['0'].hitCount).toEqual(1); expect(functionCounters['(anonymous)']['2']['0'].hitCount).toEqual(1); }); it('can disambiguate two functions on same line with different params', function () { let functionCounters = Coverage._functionsToFunctionCounters('script', [ { key: '(anonymous):1:0', line: 1, n_params: 0 }, { key: '(anonymous):1:1', line: 1, n_params: 1 } ]); Coverage._incrementFunctionCounters(functionCounters, null, '(anonymous)', 1, 0); Coverage._incrementFunctionCounters(functionCounters, null, '(anonymous)', 1, 1); expect(functionCounters['(anonymous)']['1']['0'].hitCount).toEqual(1); expect(functionCounters['(anonymous)']['1']['1'].hitCount).toEqual(1); }); it('can disambiguate two functions on same line by guessing closest params', function () { let functionCounters = Coverage._functionsToFunctionCounters('script', [ { key: '(anonymous):1:0', line: 1, n_params: 0 }, { key: '(anonymous):1:3', line: 1, n_params: 3 } ]); /* Eg, we called the function with 3 params with just two arguments. We * should be able to work out that we probably intended to call the * latter function as opposed to the former. */ Coverage._incrementFunctionCounters(functionCounters, null, '(anonymous)', 1, 2); expect(functionCounters['(anonymous)']['1']['0'].hitCount).toEqual(0); expect(functionCounters['(anonymous)']['1']['3'].hitCount).toEqual(1); }); it('increments for function on earlier start line', function () { let ast = { body: [{ type: 'FunctionDeclaration', id: { name: 'name' }, loc: { start: { line: 1 } }, params: [], body: { type: 'BlockStatement', body: [] } }] }; let detectedFunctions = Coverage.functionsForAST(ast); let knownFunctionsArray = Coverage._populateKnownFunctions(detectedFunctions, 3); let functionCounters = Coverage._functionsToFunctionCounters('script', detectedFunctions); /* We're entering at line two, but the function definition was actually * at line one */ Coverage._incrementFunctionCounters(functionCounters, knownFunctionsArray, 'name', 2, 0); expect(functionCounters.name['1']['0'].hitCount).toEqual(1); }); it('throws an error on unexpected function', function () { let ast = { body: [{ type: 'FunctionDeclaration', id: { name: 'name' }, loc: { start: { line: 1 } }, params: [], body: { type: 'BlockStatement', body: [] } }] }; let detectedFunctions = Coverage.functionsForAST(ast); let knownFunctionsArray = Coverage._populateKnownFunctions(detectedFunctions, 3); let functionCounters = Coverage._functionsToFunctionCounters('script', detectedFunctions); /* We're entering at line two, but the function definition was actually * at line one */ expect(() => { Coverage._incrementFunctionCounters(functionCounters, knownFunctionsArray, 'doesnotexist', 2, 0); }).toThrow(); }); it('throws if line out of range', function () { let expressionCounters = [ undefined, 0 ]; expect(() => { Coverage._incrementExpressionCounters(expressionCounters, 'script', 2); }).toThrow(); }); it('increments if in range', function () { let expressionCounters = [ undefined, 0 ]; Coverage._incrementExpressionCounters(expressionCounters, 'script', 1); expect(expressionCounters[1]).toEqual(1); }); it('warns if we hit a non-executable line', function () { spyOn(window, 'log'); let expressionCounters = [ undefined, 0, undefined ]; Coverage._incrementExpressionCounters(expressionCounters, 'script', 2, true); expect(window.log).toHaveBeenCalledWith("script:2 Executed line " + "previously marked non-executable by Reflect"); expect(expressionCounters[2]).toEqual(1); }); }); describe('Coverage statistics container', function () { const MockFiles = { 'filename': "function f() {\n" + " return 1;\n" + "}\n" + "if (f())\n" + " f = 0;\n" + "\n", 'uncached': "function f() {\n" + " return 1;\n" + "}\n" }; const MockFilenames = Object.keys(MockFiles).concat(['nonexistent']); beforeEach(function () { Coverage.getFileContents = jasmine.createSpy('getFileContents').and.callFake(f => MockFiles[f]); Coverage.getFileChecksum = jasmine.createSpy('getFileChecksum').and.returnValue('abcd'); Coverage.getFileModificationTime = jasmine.createSpy('getFileModificationTime').and.returnValue([1, 2]); }); it('fetches valid statistics for file', function () { let container = new Coverage.CoverageStatisticsContainer(MockFilenames); let statistics = container.fetchStatistics('filename'); expect(statistics).toBeDefined(); let files = container.getCoveredFiles(); expect(files).toEqual(['filename']); }); it('throws for nonexisting file', function () { let container = new Coverage.CoverageStatisticsContainer(MockFilenames); expect(() => container.fetchStatistics('nonexistent')).toThrow(); }); const MockCache = '{ \ "filename": { \ "mtime": [1, 2], \ "checksum": null, \ "lines": [2, 4, 5], \ "branches": [ \ { \ "point": 4, \ "exits": [5] \ } \ ], \ "functions": [ \ { \ "key": "f:1:0", \ "line": 1 \ } \ ] \ } \ }'; describe('with cache', function () { let container; beforeEach(function () { spyOn(Coverage, '_fetchCountersFromReflection').and.callThrough(); container = new Coverage.CoverageStatisticsContainer(MockFilenames, MockCache); }); it('fetches counters from cache', function () { container.fetchStatistics('filename'); expect(Coverage._fetchCountersFromReflection).not.toHaveBeenCalled(); }); it('fetches counters from reflection if missed', function () { container.fetchStatistics('uncached'); expect(Coverage._fetchCountersFromReflection).toHaveBeenCalled(); }); it('cache is not stale if all hit', function () { container.fetchStatistics('filename'); expect(container.staleCache()).toBeFalsy(); }); it('cache is stale if missed', function () { container.fetchStatistics('uncached'); expect(container.staleCache()).toBeTruthy(); }); }); describe('coverage counters from cache', function () { let container, statistics; let containerWithNoCaching, statisticsWithNoCaching; beforeEach(function () { container = new Coverage.CoverageStatisticsContainer(MockFilenames, MockCache); statistics = container.fetchStatistics('filename'); containerWithNoCaching = new Coverage.CoverageStatisticsContainer(MockFilenames); statisticsWithNoCaching = containerWithNoCaching.fetchStatistics('filename'); }); it('have same executable lines as reflection', function () { expect(statisticsWithNoCaching.expressionCounters) .toEqual(statistics.expressionCounters); }); it('have same branch exits as reflection', function () { /* Branch starts on line 4 */ expect(statisticsWithNoCaching.branchCounters[4].exits[0].line) .toEqual(statistics.branchCounters[4].exits[0].line); }); it('have same branch points as reflection', function () { expect(statisticsWithNoCaching.branchCounters[4].point) .toEqual(statistics.branchCounters[4].point); }); it('have same function keys as reflection', function () { /* Functions start on line 1 */ expect(Object.keys(statisticsWithNoCaching.functionCounters)) .toEqual(Object.keys(statistics.functionCounters)); }); }); }); cjs-3.6.1/installed-tests/js/testEverythingBasic.js000066400000000000000000000611431320401450000223510ustar00rootroot00000000000000const Regress = imports.gi.Regress; const WarnLib = imports.gi.WarnLib; // We use Gio to have some objects that we know exist const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Lang = imports.lang; 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"); let val = Limits[bytes][limit]; expect(Regress[method_stem + bytes](val)).toBe(val); } ['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('Gjs', GLib.LogLevelFlags.LEVEL_WARNING, "*cannot be safely stored*"); GLib.test_expect_message('Gjs', 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('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 () { 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'); }); 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(); } }); 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(); }); }); }); cjs-3.6.1/installed-tests/js/testEverythingEncapsulated.js000066400000000000000000000233351320401450000237410ustar00rootroot00000000000000const 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(); }); }); 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-3.6.1/installed-tests/js/testExceptions.js000066400000000000000000000145631320401450000214100ustar00rootroot00000000000000const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Lang = imports.lang; const Foo = new Lang.Class({ Name: 'Foo', Extends: GObject.Object, Properties: { 'prop': GObject.ParamSpec.string('prop', '', '', GObject.ParamFlags.READWRITE, '') }, set prop(v) { throw new Error('set'); }, get prop() { throw new Error('get'); } }); const Bar = new Lang.Class({ Name: 'Bar', Extends: GObject.Object, Properties: { 'prop': GObject.ParamSpec.string('prop', '', '', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, ''), } }); 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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('Gjs', 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-3.6.1/installed-tests/js/testFormat.js000066400000000000000000000037421320401450000205140ustar00rootroot00000000000000const 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-3.6.1/installed-tests/js/testFundamental.js000066400000000000000000000007001320401450000215110ustar00rootroot00000000000000const 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-3.6.1/installed-tests/js/testGDBus.js000066400000000000000000000335521320401450000202320ustar00rootroot00000000000000const 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 = ' \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ '; /* Test is the actual object exporting the dbus methods */ function Test() { this._init(); } const PROP_READ_WRITE_INITIAL_VALUE = 58; const PROP_WRITE_ONLY_INITIAL_VALUE = "Initial value"; Test.prototype = { _init: function(){ 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: function(args) { return { hello: new GLib.Variant('s', 'world') }; }, nonJsonFrobateStuff: function(i) { if (i == 42) { return "42 it is!"; } else { return "Oops"; } }, alwaysThrowException: function() { throw Error("Exception!"); }, thisDoesNotExist: function () { /* We'll remove this later! */ }, noInParameter: function() { return "Yes!"; }, multipleInArgs: function(a, b, c, d, e) { return a + " " + b + " " + c + " " + d + " " + e; }, emitSignal: function() { this._impl.emit_signal('signalFoo', GLib.Variant.new('(s)', [ "foobar" ])); }, noReturnValue: function() { /* 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: function() { return [ "Hello", "World", "!" ]; }, oneArrayOut: function() { return [ "Hello", "World", "!" ]; }, /* Same thing again. In this case multipleArrayOut is "asas", * while arrayOfArrayOut is "aas". */ multipleArrayOut: function() { return [[ "Hello", "World" ], [ "World", "Hello" ]]; }, arrayOfArrayOut: function() { return [[ "Hello", "World" ], [ "World", "Hello" ]]; }, arrayOutBadSig: function() { return Symbol('Hello World!'); }, byteArrayEcho: function(binaryString) { return binaryString; }, byteEcho: function(aByte) { return aByte; }, dictEcho: function(dict) { return dict; }, /* This one is implemented asynchronously. Returns * the input arguments */ echoAsync: function(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: function () { 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(); }); /* 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('Gjs', 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('Gjs', 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-3.6.1/installed-tests/js/testGIMarshalling.js000066400000000000000000000571131320401450000217460ustar00rootroot00000000000000const 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; const Lang = imports.lang; 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(); }); }); function callback_return_value_only() { return 42; } function callback_one_out_parameter() { return 43; } function callback_multiple_out_parameters() { return [44, 45]; } function callback_return_value_and_one_out_parameter() { return [46, 47]; } function callback_return_value_and_multiple_out_parameters() { return [48, 49, 50]; } function callback_array_out_parameter() { return [50, 51]; } describe('Callback', function () { it('marshals a return value', function () { expect(GIMarshallingTests.callback_return_value_only(callback_return_value_only)) .toEqual(42); }); it('marshals one out parameter', function () { expect(GIMarshallingTests.callback_one_out_parameter(callback_one_out_parameter)) .toEqual(43); }); it('marshals multiple out parameters', function () { expect(GIMarshallingTests.callback_multiple_out_parameters(callback_multiple_out_parameters)) .toEqual([44, 45]); }); it('marshals a return value and one out parameter', function () { expect(GIMarshallingTests.callback_return_value_and_one_out_parameter(callback_return_value_and_one_out_parameter)) .toEqual([46, 47]); }); it('marshals a return value and multiple out parameters', function () { expect(GIMarshallingTests.callback_return_value_and_multiple_out_parameters(callback_return_value_and_multiple_out_parameters)) .toEqual([48, 49, 50]); }); xit('marshals an array out parameter', function () { expect(GIMarshallingTests.callback_array_out_parameter(callback_array_out_parameter)) .toEqual([50, 51]); }).pend('Function not added to gobject-introspection test suite yet'); }); const VFuncTester = new Lang.Class({ Name: 'VFuncTester', Extends: GIMarshallingTests.Object, vfunc_vfunc_return_value_only: callback_return_value_only, vfunc_vfunc_one_out_parameter: callback_one_out_parameter, vfunc_vfunc_multiple_out_parameters: callback_multiple_out_parameters, vfunc_vfunc_return_value_and_one_out_parameter: callback_return_value_and_one_out_parameter, vfunc_vfunc_return_value_and_multiple_out_parameters: callback_return_value_and_multiple_out_parameters, vfunc_vfunc_array_out_parameter: callback_array_out_parameter, }); 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]); }); }); 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-3.6.1/installed-tests/js/testGLib.js000066400000000000000000000032221320401450000200720ustar00rootroot00000000000000const 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-3.6.1/installed-tests/js/testGObjectClass.js000066400000000000000000000254721320401450000215730ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- imports.gi.versions.Gtk = '3.0'; const Lang = imports.lang; const GObject = imports.gi.GObject; const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; 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'); }); // 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(); }); }); cjs-3.6.1/installed-tests/js/testGObjectInterface.js000066400000000000000000000336761320401450000224330ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Lang = imports.lang; const Mainloop = imports.mainloop; 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('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 () { expect(GObjectImplementingGObjectInterface.toString()) .toEqual('[object GObjectClass for GObjectImplementingGObjectInterface]'); let obj = new GObjectImplementingGObjectInterface(); expect(obj.constructor).toEqual(GObjectImplementingGObjectInterface); expect(obj.constructor.toString()) .toEqual('[object GObjectClass for 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 }); }); cjs-3.6.1/installed-tests/js/testGTypeClass.js000066400000000000000000000036711320401450000213030ustar00rootroot00000000000000// 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 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-3.6.1/installed-tests/js/testGettext.js000066400000000000000000000010671320401450000207060ustar00rootroot00000000000000// -*- 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-3.6.1/installed-tests/js/testGtk.js000077500000000000000000000073311320401450000200120ustar00rootroot00000000000000imports.gi.versions.Gtk = '3.0'; const ByteArray = imports.byteArray; const Gtk = imports.gi.Gtk; const Lang = imports.lang; // This is ugly here, but usually it would be in a resource 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', // _init: function(params) { // this.parent(params); // }, testChildrenExist: function () { this._internalLabel = this.get_template_child(MyComplexGtkSubclass, 'label-child'); expect(this._internalLabel).not.toBeNull(); expect(this.label_child2).not.toBeNull(); expect(this._internal_label_child).not.toBeNull(); } }); 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'], // _init: function(params) { // this.parent(params); // }, testChildrenExist: function () { expect(this.label_child).not.toBeNull(); expect(this.label_child2).not.toBeNull(); expect(this._internal_label_child).not.toBeNull(); } }); 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('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-3.6.1/installed-tests/js/testImporter.js000066400000000000000000000146271320401450000210710ustar00rootroot00000000000000describe('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('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(foobar.toString()).toEqual('[GjsModule foobar]'); expect(subFoobar.toString()).toEqual('[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('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'); }); }); }); cjs-3.6.1/installed-tests/js/testInterface.js000066400000000000000000000277761320401450000212010ustar00rootroot00000000000000// -*- mode: js; indent-tabs-mode: nil -*- const Lang = imports.lang; 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('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]'); }); }); cjs-3.6.1/installed-tests/js/testLang.js000066400000000000000000000056461320401450000201520ustar00rootroot00000000000000// tests for imports.lang module // except for Lang.Class and Lang.Interface, which are tested in separate files 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 () { const Obj = new Lang.Class({ Name: 'Obj', callback: function () { return true; }, }); let o; beforeEach(function () { o = new Obj(); 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-3.6.1/installed-tests/js/testLocale.js000066400000000000000000000030311320401450000204520ustar00rootroot00000000000000describe('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-3.6.1/installed-tests/js/testMainloop.js000066400000000000000000000060061320401450000210360ustar00rootroot00000000000000const 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-3.6.1/installed-tests/js/testMetaClass.js000066400000000000000000000063531320401450000211410ustar00rootroot00000000000000// -*- 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); }); }); cjs-3.6.1/installed-tests/js/testNamespace.js000066400000000000000000000003001320401450000211430ustar00rootroot00000000000000const Regress = imports.gi.Regress; describe('GI repository namespace', function () { it('supplies a name', function () { expect(Regress.__name__).toEqual('Regress'); }); }); cjs-3.6.1/installed-tests/js/testParamSpec.js000066400000000000000000000027741320401450000211430ustar00rootroot00000000000000const 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); cjs-3.6.1/installed-tests/js/testSignals.js000066400000000000000000000076421320401450000206670ustar00rootroot00000000000000const 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('Object with signals', function () { testSignals(Foo); }); const FooWithoutSignals = new Lang.Class({ Name: 'FooWithoutSignals', _init: function () {}, }); 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('Gjs', 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('Gjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(2); expect(bar2).toHaveBeenCalledTimes(2); }); }); } cjs-3.6.1/installed-tests/js/testSystem.js000066400000000000000000000015711320401450000205460ustar00rootroot00000000000000const 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(20000); }); }); 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-3.6.1/installed-tests/js/testTweener.js000066400000000000000000000263321320401450000206750ustar00rootroot00000000000000const 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); }); }); cjs-3.6.1/installed-tests/js/testself.js000066400000000000000000000016441320401450000202140ustar00rootroot00000000000000describe('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-3.6.1/installed-tests/minijasmine.cpp000066400000000000000000000120031320401450000204070ustar00rootroot00000000000000/* -*- 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 tests are known to fail in the presence of the JIT; * we leak objects. * https://bugzilla.gnome.org/show_bug.cgi?id=616193 */ g_setenv("GJS_DISABLE_JIT", "1", false); /* 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-3.6.1/installed-tests/minijasmine.test.in000066400000000000000000000001721320401450000212150ustar00rootroot00000000000000[Test] Type=session Exec=@pkglibexecdir@/installed-tests/minijasmine @pkglibexecdir@/installed-tests/js/@name@ Output=TAP cjs-3.6.1/installed-tests/script.test.in000066400000000000000000000001261320401450000202150ustar00rootroot00000000000000[Test] Type=session Exec=sh @pkglibexecdir@/installed-tests/scripts/@name@ Output=TAP cjs-3.6.1/installed-tests/scripts/000077500000000000000000000000001320401450000170735ustar00rootroot00000000000000cjs-3.6.1/installed-tests/scripts/testCommandLine.sh000077500000000000000000000103051320401450000225170ustar00rootroot00000000000000#!/bin/sh if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR"/gjs-console else gjs=gjs-console fi # 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 total=0 report () { exit_code=$? total=`expr $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=`expr $total + 1` if test $exit_code -ne 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } # 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" # 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 'Gjs-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 'Gjs-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 'Gjs-WARNING.*--coverage-output' report "--coverage-output after script should succeed but give a warning" rm -f foo/coverage.lcov # --version works "$gjs" --version >/dev/null report "--version should work" test -n "`"$gjs" --version`" report "--version should print something" # --version after a script goes to the script script='if(ARGV[0] !== "--version") imports.system.exit(1)' "$gjs" -c "$script" --version report "--version after -c should be passed to script" test -z "`"$gjs" -c "$script" --version`" report "--version after -c should not print anything" rm -f exit.js help.js echo "1..$total" cjs-3.6.1/installed-tests/scripts/testWarnings.sh000077500000000000000000000013261320401450000221240ustar00rootroot00000000000000#!/bin/sh if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR"/gjs-console else gjs=gjs-console fi total=0 report () { exit_code=$? total=`expr $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" echo "1..$total" cjs-3.6.1/libgjs-private/000077500000000000000000000000001320401450000152075ustar00rootroot00000000000000cjs-3.6.1/libgjs-private/gjs-gdbus-wrapper.cpp000066400000000000000000000322301320401450000212560ustar00rootroot00000000000000/* -*- 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; }; G_DEFINE_TYPE(GjsDBusImplementation, gjs_dbus_implementation, G_TYPE_DBUS_INTERFACE_SKELETON) 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); g_type_class_add_private(klass, sizeof(GjsDBusImplementationPrivate)); 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-3.6.1/libgjs-private/gjs-gdbus-wrapper.h000066400000000000000000000055461320401450000207350ustar00rootroot00000000000000/* -*- 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-3.6.1/libgjs-private/gjs-gtk-util.c000066400000000000000000000052711320401450000177010ustar00rootroot00000000000000/* -*- 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-3.6.1/libgjs-private/gjs-gtk-util.h000066400000000000000000000031721320401450000177040ustar00rootroot00000000000000/* -*- 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-3.6.1/libgjs-private/gjs-util.cpp000066400000000000000000000065171320401450000174620ustar00rootroot00000000000000/* -*- 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-3.6.1/libgjs-private/gjs-util.h000066400000000000000000000046241320401450000171240ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/000077500000000000000000000000001320401450000137355ustar00rootroot00000000000000cjs-3.6.1/modules/_lie.js000066400000000000000000000211401320401450000152010ustar00rootroot00000000000000// jscs:disable validateIndentation (function () { 'use strict'; const GLib = imports.gi.GLib; var reqs = { immediate: function () { return function (func, priority=GLib.PRIORITY_DEFAULT_IDLE) { GLib.idle_add(priority, function () { func(); return GLib.SOURCE_REMOVE; }); }; }, }; function require(req) { return reqs[req](); } var process = { emit: function (event, error) { if (event === 'unhandledRejection') { log('Unhandled rejection'); throw error; } }, }; // Copyright (c) 2014 Calvin Metcalf // // 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. // BEGIN CODE FROM lie/lib/index.js // https://raw.githubusercontent.com/calvinmetcalf/lie/master/lib/index.js // 'use strict'; // Moved for GJS var immediate = require('immediate'); /* istanbul ignore next */ function INTERNAL() {} var handlers = {}; var REJECTED = ['REJECTED']; var FULFILLED = ['FULFILLED']; var PENDING = ['PENDING']; /* istanbul ignore else */ if (!process.browser) { // in which we actually take advantage of JS scoping var UNHANDLED = ['UNHANDLED']; } // module.exports = Promise; // removed for GJS function Promise(resolver) { if (typeof this !== 'object') { throw new TypeError('this must be an object'); } if (typeof resolver !== 'function') { throw new TypeError('resolver must be a function'); } if (this instanceof Promise && typeof this.state !== 'undefined') { throw new TypeError('this must not be an already-constructed promise'); } this.state = PENDING; this.queue = []; this.outcome = void 0; /* istanbul ignore else */ if (!process.browser) { this.handled = UNHANDLED; } if (resolver !== INTERNAL) { safelyResolveThenable(this, resolver); } } Promise.prototype.catch = function (onRejected) { return this.then(null, onRejected); }; Promise.prototype.then = function (onFulfilled, onRejected) { if (typeof onFulfilled !== 'function' && this.state === FULFILLED || typeof onRejected !== 'function' && this.state === REJECTED) { return this; } var promise = new this.constructor(INTERNAL); /* istanbul ignore else */ if (!process.browser) { if (this.handled === UNHANDLED) { this.handled = null; } } if (this.state !== PENDING) { var resolver = this.state === FULFILLED ? onFulfilled : onRejected; unwrap(promise, resolver, this.outcome); } else { this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); } return promise; }; function QueueItem(promise, onFulfilled, onRejected) { this.promise = promise; if (typeof onFulfilled === 'function') { this.onFulfilled = onFulfilled; this.callFulfilled = this.otherCallFulfilled; } if (typeof onRejected === 'function') { this.onRejected = onRejected; this.callRejected = this.otherCallRejected; } } QueueItem.prototype.callFulfilled = function (value) { handlers.resolve(this.promise, value); }; QueueItem.prototype.otherCallFulfilled = function (value) { unwrap(this.promise, this.onFulfilled, value); }; QueueItem.prototype.callRejected = function (value) { handlers.reject(this.promise, value); }; QueueItem.prototype.otherCallRejected = function (value) { unwrap(this.promise, this.onRejected, value); }; function unwrap(promise, func, value) { immediate(function () { var returnValue; try { returnValue = func(value); } catch (e) { handlers.reject(promise, e); // Changed for GJS return; // Added for GJS } if (returnValue === promise) { handlers.reject(promise, new TypeError('Cannot resolve promise with itself')); } else { handlers.resolve(promise, returnValue); } }); } handlers.resolve = function (self, value) { var result = tryCatch(getThen, value); if (result.status === 'error') { return handlers.reject(self, result.value); } var thenable = result.value; if (thenable) { safelyResolveThenable(self, thenable); } else { self.state = FULFILLED; self.outcome = value; var i = -1; var len = self.queue.length; while (++i < len) { self.queue[i].callFulfilled(value); } } return self; }; handlers.reject = function (self, error) { self.state = REJECTED; self.outcome = error; /* istanbul ignore else */ if (!process.browser) { if (self.handled === UNHANDLED) { immediate(function () { if (self.handled === UNHANDLED) { process.emit('unhandledRejection', error, self); } }); } } var i = -1; var len = self.queue.length; while (++i < len) { self.queue[i].callRejected(error); } return self; }; function getThen(obj) { // Make sure we only access the accessor once as required by the spec var then = obj && obj.then; if (obj && typeof obj === 'object' && typeof then === 'function') { return function appyThen() { then.apply(obj, arguments); }; } return undefined; // added for GJS } function safelyResolveThenable(self, thenable) { // Either fulfill, reject or reject with error var called = false; function onError(value) { if (called) { return; } called = true; handlers.reject(self, value); } function onSuccess(value) { if (called) { return; } called = true; handlers.resolve(self, value); } function tryToUnwrap() { thenable(onSuccess, onError); } var result = tryCatch(tryToUnwrap); if (result.status === 'error') { onError(result.value); } } function tryCatch(func, value) { var out = {}; try { out.value = func(value); out.status = 'success'; } catch (e) { out.status = 'error'; out.value = e; } return out; } Promise.resolve = resolve; function resolve(value) { if (value instanceof this) { return value; } return handlers.resolve(new this(INTERNAL), value); } Promise.reject = reject; function reject(reason) { var promise = new this(INTERNAL); return handlers.reject(promise, reason); } Promise.all = all; function all(iterable) { var self = this; if (Object.prototype.toString.call(iterable) !== '[object Array]') { return this.reject(new TypeError('must be an array')); } var len = iterable.length; var called = false; if (!len) { return this.resolve([]); } var values = new Array(len); var resolved = 0; var i = -1; var promise = new this(INTERNAL); while (++i < len) { allResolver(iterable[i], i); } // return promise; // moved for GJS function allResolver(value, i) { self.resolve(value).then(resolveFromAll, function (error) { if (!called) { called = true; handlers.reject(promise, error); } }); function resolveFromAll(outValue) { values[i] = outValue; if (++resolved === len && !called) { called = true; handlers.resolve(promise, values); } } } return promise; // Added for GJS } Promise.race = race; function race(iterable) { var self = this; if (Object.prototype.toString.call(iterable) !== '[object Array]') { return this.reject(new TypeError('must be an array')); } var len = iterable.length; var called = false; if (!len) { return this.resolve([]); } var i = -1; var promise = new this(INTERNAL); while (++i < len) { resolver(iterable[i]); } // return promise; // Moved for GJS function resolver(value) { self.resolve(value).then(function (response) { if (!called) { called = true; handlers.resolve(promise, response); } }, function (error) { if (!called) { called = true; handlers.reject(promise, error); } }); } return promise; // Added for GJS } // END CODE FROM lie/lib/index.js return Promise; })(); cjs-3.6.1/modules/cairo-context.cpp000066400000000000000000001167641320401450000172370ustar00rootroot00000000000000/* -*- 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; if (!gjs_parse_call_args(context, "setDash", argv, "of", "dashes", &dashes, "offset", &offset)) return false; if (!JS_IsArrayObject(context, dashes)) { 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); char *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); g_free(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); char *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); g_free(family); 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-3.6.1/modules/cairo-gradient.cpp000066400000000000000000000072731320401450000173420ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-image-surface.cpp000066400000000000000000000163471320401450000202570ustar00rootroot00000000000000/* -*- 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); char *filename; cairo_surface_t *surface; if (!gjs_parse_call_args(context, "createFromPNG", argv, "s", "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-3.6.1/modules/cairo-linear-gradient.cpp000066400000000000000000000070241320401450000206040ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-module.h000066400000000000000000000025601320401450000164710ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-path.cpp000066400000000000000000000067741320401450000165060ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-pattern.cpp000066400000000000000000000137351320401450000172220ustar00rootroot00000000000000/* -*- 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 != NULL); 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-3.6.1/modules/cairo-pdf-surface.cpp000066400000000000000000000075321320401450000177420ustar00rootroot00000000000000/* -*- 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) char *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")) { g_free(filename); return false; } gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); g_free(filename); 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-3.6.1/modules/cairo-private.h000066400000000000000000000213261320401450000166570ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-ps-surface.cpp000066400000000000000000000077041320401450000176140ustar00rootroot00000000000000/* -*- 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) char *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")) { g_free(filename); return false; } gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); g_free(filename); 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-3.6.1/modules/cairo-radial-gradient.cpp000066400000000000000000000072571320401450000205760ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-region.cpp000066400000000000000000000262021320401450000170210ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-solid-pattern.cpp000066400000000000000000000107201320401450000203210ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-surface-pattern.cpp000066400000000000000000000143421320401450000206430ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo-surface.cpp000066400000000000000000000210301320401450000171600ustar00rootroot00000000000000/* -*- 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); char *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) { g_free(filename); return false; } cairo_surface_write_to_png(surface, filename); g_free(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 != NULL); 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-3.6.1/modules/cairo-svg-surface.cpp000066400000000000000000000075321320401450000177700ustar00rootroot00000000000000/* -*- 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) char *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")) { g_free(filename); return false; } gjs_cairo_surface_construct(context, object, surface); cairo_surface_destroy(surface); g_free(filename); 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-3.6.1/modules/cairo.cpp000066400000000000000000000064431320401450000155450ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/cairo.js000066400000000000000000000055471320401450000154030ustar00rootroot00000000000000// 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; const Antialias = { DEFAULT: 0, NONE: 1, GRAY: 2, SUBPIXEL: 3 }; const Content = { COLOR : 0x1000, ALPHA : 0x2000, COLOR_ALPHA : 0x3000 }; const Extend = { NONE : 0, REPEAT : 1, REFLECT : 2, PAD : 3 }; const FillRule = { WINDING: 0, EVEN_ODD: 1 }; const Filter = { FAST : 0, GOOD : 1, BEST : 2, NEAREST : 3, BILINEAR : 4, GAUSSIAN : 5 }; const FontSlant = { NORMAL: 0, ITALIC: 1, OBLIQUE: 2 }; const FontWeight = { NORMAL : 0, BOLD : 1 }; const Format = { ARGB32 : 0, RGB24 : 1, A8 : 2, A1 : 3, // The value of 4 is reserved by a deprecated enum value RGB16_565: 5 }; const LineCap = { BUTT: 0, ROUND: 1, SQUASH: 2 }; const LineJoin = { MITER: 0, ROUND: 1, BEVEL: 2 }; const 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 }; const PatternType = { SOLID : 0, SURFACE : 1, LINEAR : 2, RADIAL : 3 }; const 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-3.6.1/modules/console.cpp000066400000000000000000000174671320401450000161220ustar00rootroot00000000000000/* -*- 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/jsapi-private.h" #include "cjs/jsapi-wrapper.h" static void gjs_console_error_reporter(JSContext *cx, const char *message, JSErrorReport *report) { int i, j, k, n; char *prefix, *tmp; const char *ctmp; if (!report) { fprintf(stderr, "%s\n", message); return; } prefix = NULL; if (report->filename) prefix = g_strdup_printf("%s:", report->filename); if (report->lineno) { tmp = prefix; prefix = g_strdup_printf("%s%u: ", tmp ? tmp : "", report->lineno); g_free(tmp); } if (JSREPORT_IS_WARNING(report->flags)) { tmp = prefix; prefix = g_strdup_printf("%s%swarning: ", tmp ? tmp : "", JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); g_free(tmp); } /* embedded newlines -- argh! */ while ((ctmp = strchr(message, '\n')) != NULL) { 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 (!report->linebuf) { fputc('\n', stderr); goto out; } /* report->linebuf usually ends with a newline. */ n = strlen(report->linebuf); fprintf(stderr, ":\n%s%s%s%s", prefix, report->linebuf, (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n", prefix); n = ((char*)report->tokenptr) - ((char*) report->linebuf); for (i = j = 0; i < n; i++) { if (report->linebuf[i] == '\t') { for (k = (j + 8) & ~7; j < k; j++) { fputc('.', stderr); } continue; } fputc('.', stderr); j++; } fputs("^\n", stderr); out: g_free(prefix); } #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 static bool gjs_console_interact(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); bool eof = false; JS::RootedValue result(context); JS::RootedString str(context); JS::RootedObject global(context, gjs_get_import_global(context)); GString *buffer = NULL; char *temp_buf = NULL; int lineno; int startline; FILE *file = stdin; JS_SetErrorReporter(JS_GetRuntime(context), gjs_console_error_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)); JS::CompileOptions options(context); options.setUTF8(true) .setFileAndLine("typein", startline); if (!JS::Evaluate(context, global, options, buffer->str, buffer->len, &result)) { /* 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. */ if (!JS_IsExceptionPending(context)) { argv.rval().set(result); return false; } } gjs_schedule_gc_if_needed(context); if (JS_GetPendingException(context, &result)) { str = JS::ToString(context, result); JS_ClearPendingException(context); } else if (result.isUndefined()) { goto next; } else { str = JS::ToString(context, result); } if (str) { char *display_str; display_str = gjs_value_debug_string(context, result); if (display_str != NULL) { g_fprintf(stdout, "%s\n", display_str); g_free(display_str); } } next: g_string_free(buffer, true); } while (!eof); g_fprintf(stdout, "\n"); if (file != stdin) fclose(file); 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-3.6.1/modules/console.h000066400000000000000000000027221320401450000155530ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/coverage.js000066400000000000000000001033011320401450000160640ustar00rootroot00000000000000/* * Copyright (c) 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 */ function getSubNodesForNode(node) { let subNodes = []; switch (node.type) { /* These statements have a single body */ case 'LabelledStatement': case 'WithStatement': case 'FunctionDeclaration': case 'FunctionExpression': case 'CatchClause': subNodes.push(node.body); break; case 'LetStatement': Array.prototype.push.apply(subNodes, node.head); subNodes.push(node.body); break; case 'WhileStatement': case 'DoWhileStatement': subNodes.push(node.body); subNodes.push(node.test); break; case 'ForStatement': if (node.init !== null) subNodes.push(node.init); if (node.test !== null) subNodes.push(node.test); if (node.update !== null) subNodes.push(node.update); subNodes.push(node.body); break; case 'ForInStatement': if (node.each) subNodes.push(node.left); subNodes.push(node.right, node.body); break; case 'ForOfStatement': subNodes.push(node.left, node.right, node.body); break; case 'BlockStatement': Array.prototype.push.apply(subNodes, node.body); break; case 'ThrowStatement': case 'ReturnStatement': case 'YieldExpression': if (node.argument !== null) subNodes.push(node.argument); break; case 'ExpressionStatement': subNodes.push(node.expression); break; case 'AssignmentExpression': case 'BinaryExpression': case 'LogicalExpression': subNodes.push(node.left, node.right); break; case 'ConditionalExpression': subNodes.push(node.test, node.consequent, node.alternate); break; case 'ObjectExpression': node.properties.forEach(function(prop) { subNodes.push(prop.value); }); break; case 'ArrayExpression': node.elements.forEach(function(elem) { if (elem !== null) subNodes.push(elem); }); break; case 'ArrowExpression': Array.prototype.push.apply(subNodes, node.defaults); subNodes.push(node.body); break; case 'SequenceExpression': Array.prototype.push.apply(subNodes, node.expressions); break; case 'UnaryExpression': case 'UpdateExpression': subNodes.push(node.argument); break; case 'ComprehensionExpression': case 'GeneratorExpression': subNodes.push(node.body); Array.prototype.push.apply(subNodes, node.blocks); if (node.filter !== null) subNodes.push(node.filter); break; case 'ComprehensionBlock': subNodes.push(node.right); break; /* It is very possible that there might be something * interesting in the function arguments, so we need to * walk them too */ case 'NewExpression': case 'CallExpression': Array.prototype.push.apply(subNodes, node.arguments); subNodes.push(node.callee); break; /* These statements might have multiple different bodies * depending on whether or not they were entered */ case 'IfStatement': subNodes = [node.test, node.consequent]; if (node.alternate !== null) subNodes.push(node.alternate); break; case 'TryStatement': subNodes = [node.block]; if (node.handler !== null) subNodes.push(node.handler); if (node.finalizer !== null) subNodes.push(node.finalizer); break; case 'SwitchStatement': for (let caseClause of node.cases) { caseClause.consequent.forEach(function(expression) { subNodes.push(expression); }); } break; case 'VariableDeclaration': Array.prototype.push.apply(subNodes, node.declarations); break; case 'VariableDeclarator': if (node.init !== null) subNodes.push(node.init); break; case 'MemberExpression': subNodes.push(node.object); if (node.computed) subNodes.push(node.property); break; } return subNodes; } function collectForSubNodes(subNodes, collector) { let result = []; if (subNodes !== undefined && subNodes.length > 0) { subNodes.forEach(function(node) { let nodeResult = collector(node); if (nodeResult !== undefined) Array.prototype.push.apply(result, nodeResult); let subNodeResults = collectForSubNodes(getSubNodesForNode(node), collector); Array.prototype.push.apply(result, subNodeResults); }); } return result; } function _getFunctionKeyFromReflectedFunction(node) { let name = node.id !== null ? node.id.name : '(anonymous)'; let line = node.loc.start.line; let n_params = node.params.length; return [name, line, n_params].join(':'); } /* Unfortunately, the Reflect API doesn't give us enough information to * uniquely identify a function. A function might be anonymous, in which * case the JS engine uses some heurisitics to get a unique string identifier * but that isn't available to us here. * * There's also the edge-case where functions with the same name might be * defined within the same scope, or multiple anonymous functions might * be defined on the same line. In that case, it will look like we entered * the same function multiple times since we can't get column information * from the engine-side. * * For instance: * * 1. function f() { * function f() { * } * } * * 2. let a = function() { function(a, b) {} }; * * 3. let a = function() { function () {} } * * We can work-around case 1 by using the line numbers to get a unique identifier. * We can work-around case 2 by using the arguments length to get a unique identifier * We can't work-around case 3. The best thing we can do is warn that coverage * reports might be inaccurate as a result */ function functionsForNode(node) { let functionNames = []; switch (node.type) { case 'FunctionDeclaration': case 'FunctionExpression': case 'ArrowExpression': functionNames.push({ key: _getFunctionKeyFromReflectedFunction(node), line: node.loc.start.line, n_params: node.params.length }); } return functionNames; } function functionsForAST(ast) { return collectForSubNodes(ast.body, functionsForNode); } /* If a branch' consequent is a block statement, there's * a chance that it could start on the same line, although * that's not where execution really starts. If it is * a block statement then handle the case and go * to the first line where execution starts */ function getBranchExitStartLine(branchBodyNode) { switch (branchBodyNode.type) { case 'BlockStatement': /* Hit a block statement, but nothing inside, can never * be executed, tell the upper level to move on to the next * statement */ if (branchBodyNode.body.length === 0) return -1; /* Handle the case where we have nested block statements * that never actually get to executable code by handling * all statements within a block */ for (let statement of branchBodyNode.body) { let startLine = getBranchExitStartLine(statement); if (startLine !== -1) return startLine; } /* Couldn't find an executable line inside this block */ return -1; case 'SwitchCase': /* Hit a switch, but nothing inside, can never * be executed, tell the upper level to move on to the next * statement */ if (branchBodyNode.consequent.length === 0) return -1; /* Handle the case where we have nested block statements * that never actually get to executable code by handling * all statements within a block */ for (let statement of branchBodyNode.consequent) { let startLine = getBranchExitStartLine(statement); if (startLine !== -1) { return startLine; } } /* Couldn't find an executable line inside this block */ return -1; /* These types of statements are never executable */ case 'EmptyStatement': case 'LabelledStatement': return -1; default: break; } return branchBodyNode.loc.start.line; } function branchesForNode(node) { let branches = []; let branchExitNodes = []; switch (node.type) { case 'IfStatement': branchExitNodes.push(node.consequent); if (node.alternate !== null) branchExitNodes.push(node.alternate); break; case 'WhileStatement': case 'DoWhileStatement': branchExitNodes.push(node.body); break; case 'SwitchStatement': /* The case clauses by themselves are never executable * so find the actual exits */ Array.prototype.push.apply(branchExitNodes, node.cases); break; default: break; } let branchExitStartLines = branchExitNodes.map(getBranchExitStartLine); branchExitStartLines = branchExitStartLines.filter(function(line) { return line !== -1; }); /* Branch must have at least one exit */ if (branchExitStartLines.length) { branches.push({ point: node.loc.start.line, exits: branchExitStartLines }); } return branches; } function branchesForAST(ast) { return collectForSubNodes(ast.body, branchesForNode); } function expressionLinesForNode(statement) { let expressionLines = []; let expressionNodeTypes = ['Expression', 'Declaration', 'Statement', 'Clause', 'Literal', 'Identifier']; if (expressionNodeTypes.some(function(type) { return statement.type.indexOf(type) !== -1; })) { /* These expressions aren't executable on their own */ switch (statement.type) { case 'FunctionDeclaration': case 'LiteralExpression': break; /* Perplexingly, an empty block statement is actually executable, * push it if it is */ case 'BlockStatement': if (statement.body.length !== 0) break; expressionLines.push(statement.loc.start.line); break; default: expressionLines.push(statement.loc.start.line); break; } } return expressionLines; } function deduplicate(list) { return list.filter(function(elem, pos, self) { return self.indexOf(elem) === pos; }); } function expressionLinesForAST(ast) { let allExpressions = collectForSubNodes(ast.body, expressionLinesForNode); allExpressions = deduplicate(allExpressions); return allExpressions; } function _getNumberOfLinesForScript(scriptContents) { let scriptLines = scriptContents.split("\n"); let scriptLineCount = scriptLines.length; return scriptLineCount; } /* * The created array is a 1-1 representation of the hitcount in the filename. Each * element refers to an individual line. In order to avoid confusion, our array * is zero indexed, but the zero'th line is always ignored and the first element * refers to the first line of the file. * * A value of undefined for an element means that the line is non-executable and never actually * reached. A value of 0 means that it was executable but never reached. A positive value * indicates the hit count. * * We care about non-executable lines because we don't want to report coverage misses for * lines that could have never been executed anyways. * * The reason for using a 1-1 mapping as opposed to an array of key-value pairs for executable * lines is: * 1. Lookup speed is O(1) instead of O(log(n)) * 2. There's a possibility we might hit a line which we thought was non-executable, in which * case we can neatly handle the error by marking that line executable. A hit on a line * we thought was non-executable is not as much of a problem as noise generated by * ostensible "misses" which could in fact never be executed. * */ function _expressionLinesToCounters(expressionLines, nLines) { expressionLines.sort(function(left, right) { return left - right; }); let expressionLinesIndex = 0; let counters = new Array(nLines + 1); if (expressionLines.length === 0) return counters; for (let i = 1; i < counters.length; i++) { if (!expressionLines.hasOwnProperty(expressionLinesIndex)) continue; if (expressionLines[expressionLinesIndex] == i) { counters[i] = 0; expressionLinesIndex++; } } return counters; } /* As above, we are creating a 1-1 representation of script lines to potential branches * where each element refers to a 1-index line (with the zero'th ignored). * * Each element is a GjsCoverageBranchData which, if the line at the element * position describes a branch, will be populated with a GjsReflectedScriptBranchInfo * and an array of unsigned each specifying the hit-count for each potential branch * in the branch info */ function _branchesToBranchCounters(branches, nLines) { branches.sort(function(left, right) { return left.point - right.point; }); let branchIndex = 0; let counters = new Array(nLines + 1); if (branches.length === 0) return counters; for (let i = 1; i < counters.length; i++) { let branch = branches[branchIndex]; let branchPoint = branch.point; if (branchPoint == i) { counters[i] = { point: branchPoint, exits: branch.exits.map(function(exit) { return { line: exit, hitCount: 0 }; }), lastExit: (function() { let lastExitLine = 0; for (let exit of branch.exits) { if (lastExitLine < exit) lastExitLine = exit; } return lastExitLine; })(), hit: false }; if (++branchIndex >= branches.length) break; } } return counters; } function _functionsToFunctionCounters(script, functions) { let functionCounters = {}; functions.forEach(function(func) { let [name, line, args] = func.key.split(':'); if (functionCounters[name] === undefined) { functionCounters[name] = {}; } if (functionCounters[name][line] === undefined) { functionCounters[name][line] = {}; } if (functionCounters[name][line][args] === undefined) { functionCounters[name][line][args] = { hitCount: 0 }; } else { log(script + ':' + line + ' Function identified as ' + func.key + ' already seen in this file. Function coverage ' + 'will be incomplete.'); } }); return functionCounters; } function _populateKnownFunctions(functions, nLines) { let knownFunctions = new Array(nLines + 1); functions.forEach(function(func) { knownFunctions[func.line] = true; }); return knownFunctions; } function _identifyFunctionCounterInLinePartForDescription(linePart, nArgs) { /* There is only one potential option for this line. We might have been * called with the wrong number of arguments, but it doesn't matter. */ if (Object.getOwnPropertyNames(linePart).length === 1) return linePart[Object.getOwnPropertyNames(linePart)[0]]; /* Have to disambiguate using nArgs and we have an exact match. */ if (linePart[nArgs] !== undefined) return linePart[nArgs]; /* There are multiple options on this line. Pick the one where * we satisfy the number of arguments exactly, or failing that, * go through each and pick the closest. */ let allNArgsOptions = Object.keys(linePart).map(function(key) { return parseInt(key); }); let closest = allNArgsOptions.reduce(function(prev, current, index, array) { let nArgsOption = array[index]; if (Math.abs(nArgsOption - nArgs) < Math.abs(current - nArgs)) { return nArgsOption; } return current; }); return linePart[String(closest)]; } function _identifyFunctionCounterForDescription(functionCounters, name, line, nArgs) { let candidateCounter = functionCounters[name]; if (candidateCounter === undefined) return null; if (Object.getOwnPropertyNames(candidateCounter).length === 1) { let linePart = candidateCounter[Object.getOwnPropertyNames(candidateCounter)[0]]; return _identifyFunctionCounterInLinePartForDescription(linePart, nArgs); } let linePart = functionCounters[name][line]; if (linePart === undefined) { return null; } return _identifyFunctionCounterInLinePartForDescription(linePart, nArgs); } /** * _incrementFunctionCounters * * functionCounters: An object which is a key-value pair with the following schema: * { * "key" : { line, hitCount } * } * linesWithKnownFunctions: An array of either "true" or undefined, with true set to * each element corresponding to a line that we know has a function on it. * name: The name of the function or "(anonymous)" if it has no name * line: The line at which execution first started on this function. * nArgs: The number of arguments this function has. */ function _incrementFunctionCounters(functionCounters, linesWithKnownFunctions, name, line, nArgs) { let functionCountersForKey = _identifyFunctionCounterForDescription(functionCounters, name, line, nArgs); /* Its possible that the JS Engine might enter a funciton * at an executable line which is a little bit past the * actual definition. Roll backwards until we reach the * last known function definition line which we kept * track of earlier to see if we can find this function first */ if (functionCountersForKey === null) { do { --line; functionCountersForKey = _identifyFunctionCounterForDescription(functionCounters, name, line, nArgs); } while (linesWithKnownFunctions[line] !== true && line > 0); } if (functionCountersForKey !== null) { functionCountersForKey.hitCount++; } else { let functionKey = [name, line, nArgs].join(':'); throw new Error("expected Reflect to find function " + functionKey); } } /** * _incrementExpressionCounters * * expressonCounters: An array of either a hit count for a found * executable line or undefined for a known non-executable line. * line: an executed line * @shouldWarn: if true, print a mostly harmless warning about executing a line * that was thought non-executable. */ function _incrementExpressionCounters(expressionCounters, script, offsetLine, shouldWarn) { let expressionCountersLen = expressionCounters.length; if (offsetLine >= expressionCountersLen) throw new Error("Executed line " + offsetLine + " which was past the highest-found line " + expressionCountersLen); /* If this happens it is not a huge problem - though it does * mean that the reflection machinery is not doing its job, so we should * print a debug message about it in case someone is interested. * * The reason why we don't have a proper log is because it * is difficult to determine what the SpiderMonkey program counter * will actually pass over, especially function declarations for some * reason: * * function f(a,b) { * a = 1; * } * * In some cases, the declaration itself will be executed * but in other cases it won't be. Reflect.parse tells us that * the only two expressions on that line are a FunctionDeclaration * and BlockStatement, neither of which would ordinarily be * executed */ if (expressionCounters[offsetLine] === undefined) { if (shouldWarn) log(script + ':' + offsetLine + ' Executed line previously marked ' + 'non-executable by Reflect'); expressionCounters[offsetLine] = 0; } expressionCounters[offsetLine]++; } function _BranchTracker(branchCounters) { this._branchCounters = branchCounters; this._activeBranch = undefined; this.incrementBranchCounters = function(offsetLine) { /* Set branch exits or find a new active branch */ let activeBranch = this._activeBranch; if (activeBranch !== undefined) { activeBranch.exits.forEach(function(exit) { if (exit.line === offsetLine) { exit.hitCount++; } }); /* Only set the active branch to undefined once we're * completely outside of it, since we might be in a case statement where * we need to check every possible option before jumping to an * exit */ if (offsetLine >= activeBranch.lastExit) this._activeBranch = undefined; } let nextActiveBranch = branchCounters[offsetLine]; if (nextActiveBranch !== undefined) { this._activeBranch = nextActiveBranch; this._activeBranch.hit = true; } }; } function _convertFunctionCountersToArray(functionCounters) { let arrayReturn = []; /* functionCounters is an object so explore it to create a * set of function keys and then convert it to * an array-of-object using the key as a property * of that object */ for (let name of Object.getOwnPropertyNames(functionCounters)) { let namePart = functionCounters[name]; for (let line of Object.getOwnPropertyNames(namePart)) { let linePart = functionCounters[name][line]; for (let nArgs of Object.getOwnPropertyNames(linePart)) { let functionKey = [name, line, nArgs].join(':'); arrayReturn.push({ name: functionKey, line: Number(line), nArgs: nArgs, hitCount: linePart[nArgs].hitCount }); } } } arrayReturn.sort(function(left, right) { if (left.name < right.name) return -1; else if (left.name > right.name) return 1; else return 0; }); return arrayReturn; } /* Looks up filename in cache and fetches statistics * directly from the cache */ function _fetchCountersFromCache(filename, cache, nLines) { if (!cache) return null; if (Object.keys(cache).indexOf(filename) !== -1) { let cache_for_file = cache[filename]; if (cache_for_file.mtime) { let mtime = getFileModificationTime(filename); if (mtime[0] != cache[filename].mtime[0] || mtime[1] != cache[filename].mtime[1]) return null; } else { let checksum = getFileChecksum(filename); if (checksum != cache[filename].checksum) return null; } let functions = cache_for_file.functions; return { expressionCounters: _expressionLinesToCounters(cache_for_file.lines, nLines), branchCounters: _branchesToBranchCounters(cache_for_file.branches, nLines), functionCounters: _functionsToFunctionCounters(filename, functions), linesWithKnownFunctions: _populateKnownFunctions(functions, nLines), nLines: nLines }; } return null; } function _fetchCountersFromReflection(filename, contents, nLines) { let reflection = Reflect.parse(contents); let functions = functionsForAST(reflection); return { expressionCounters: _expressionLinesToCounters(expressionLinesForAST(reflection), nLines), branchCounters: _branchesToBranchCounters(branchesForAST(reflection), nLines), functionCounters: _functionsToFunctionCounters(filename, functions), linesWithKnownFunctions: _populateKnownFunctions(functions, nLines), nLines: nLines }; } function CoverageStatisticsContainer(prefixes, cache) { /* Copy the files array, so that it can be re-used in the tests */ let cachedASTs = cache ? JSON.parse(cache) : null; let coveredFiles = {}; let cacheMisses = 0; function createStatisticsFor(filename) { let contents = getFileContents(filename); let nLines = _getNumberOfLinesForScript(contents); let counters = _fetchCountersFromCache(filename, cachedASTs, nLines); if (counters === null) { cacheMisses++; counters = _fetchCountersFromReflection(filename, contents, nLines); } if (counters === null) throw new Error('Failed to parse and reflect file ' + filename); /* Set contents here as we don't pass it to _fetchCountersFromCache. */ counters.contents = contents; return counters; } function ensureStatisticsFor(filename) { if (!coveredFiles[filename]) coveredFiles[filename] = createStatisticsFor(filename); return coveredFiles[filename]; } this.stringify = function() { let cache_data = {}; Object.keys(coveredFiles).forEach(function(filename) { let statisticsForFilename = coveredFiles[filename]; let mtime = getFileModificationTime(filename); let cacheDataForFilename = { mtime: mtime, checksum: mtime === null ? getFileChecksum(filename) : null, lines: [], branches: [], functions: _convertFunctionCountersToArray(statisticsForFilename.functionCounters).map(function(func) { return { key: func.name, line: func.line }; }) }; /* We're using a index based loop here since we need access to the * index, since it actually represents the current line number * on the file (see _expressionLinesToCounters). */ for (let line_index = 0; line_index < statisticsForFilename.expressionCounters.length; ++line_index) { if (statisticsForFilename.expressionCounters[line_index] !== undefined) cacheDataForFilename.lines.push(line_index); if (statisticsForFilename.branchCounters[line_index] !== undefined) { let branchCounters = statisticsForFilename.branchCounters[line_index]; cacheDataForFilename.branches.push({ point: statisticsForFilename.branchCounters[line_index].point, exits: statisticsForFilename.branchCounters[line_index].exits.map(function(exit) { return exit.line; }) }); } } cache_data[filename] = cacheDataForFilename; }); return JSON.stringify(cache_data); }; this.getCoveredFiles = function() { return Object.keys(coveredFiles); }; this.fetchStatistics = function(filename) { return ensureStatisticsFor(filename); }; this.staleCache = function() { return cacheMisses > 0; }; this.deleteStatistics = function(filename) { coveredFiles[filename] = undefined; }; } /** * Main class tying together the Debugger object and CoverageStatisticsContainer. * * It isn't poissible to unit test this class because it depends on running * Debugger which in turn depends on objects injected in from another compartment */ function CoverageStatistics(prefixes, cache, shouldWarn) { this.container = new CoverageStatisticsContainer(prefixes, cache); let fetchStatistics = this.container.fetchStatistics.bind(this.container); let deleteStatistics = this.container.deleteStatistics.bind(this.container); /* 'debuggee' comes from the invocation from * a separate compartment inside of coverage.cpp */ this.dbg = new Debugger(debuggee); this.getCoveredFiles = function() { return this.container.getCoveredFiles(); }; this.getNumberOfLinesFor = function(filename) { return fetchStatistics(filename).nLines; }; this.getExecutedLinesFor = function(filename) { return fetchStatistics(filename).expressionCounters; }; this.getBranchesFor = function(filename) { return fetchStatistics(filename).branchCounters; }; this.getFunctionsFor = function(filename) { let functionCounters = fetchStatistics(filename).functionCounters; return _convertFunctionCountersToArray(functionCounters); }; this.dbg.onEnterFrame = function(frame) { let statistics; try { statistics = fetchStatistics(frame.script.url); if (!statistics) { return undefined; } } catch (e) { /* We don't care about this frame, return */ log(e.message + " " + e.stack); return undefined; } function _logExceptionAndReset(exception, callee, line) { log(exception.fileName + ":" + exception.lineNumber + " (processing " + frame.script.url + ":" + callee + ":" + line + ") - " + exception.message); log("Will not log statistics for this file"); frame.onStep = undefined; frame._branchTracker = undefined; deleteStatistics(frame.script.url); } /* Log function calls */ if (frame.callee !== null && frame.callee.callable) { let name = frame.callee.name ? frame.callee.name : "(anonymous)"; let line = frame.script.getOffsetLine(frame.offset); let nArgs = frame.callee.parameterNames.length; try { _incrementFunctionCounters(statistics.functionCounters, statistics.linesWithKnownFunctions, name, line, nArgs); } catch (e) { /* Something bad happened. Log the exception and delete * statistics for this file */ _logExceptionAndReset(e, name, line); return undefined; } } /* Upon entering the frame, the active branch is always inactive */ frame._branchTracker = new _BranchTracker(statistics.branchCounters); /* Set single-step hook */ frame.onStep = function() { /* Line counts */ let offset = this.offset; let offsetLine = this.script.getOffsetLine(offset); try { _incrementExpressionCounters(statistics.expressionCounters, frame.script.url, offsetLine, shouldWarn); this._branchTracker.incrementBranchCounters(offsetLine); } catch (e) { /* Something bad happened. Log the exception and delete * statistics for this file */ _logExceptionAndReset(e, frame.callee, offsetLine); } }; return undefined; }; this.deactivate = function() { /* This property is designed to be a one-stop-shop to * disable the debugger for this debugee, without having * to traverse all its scripts or frames */ this.dbg.enabled = false; }; this.staleCache = this.container.staleCache.bind(this.container); this.stringify = this.container.stringify.bind(this.container); } cjs-3.6.1/modules/format.js000066400000000000000000000060071320401450000155660ustar00rootroot00000000000000// -*- 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 '%'; break; 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-3.6.1/modules/gettext.js000066400000000000000000000061151320401450000157620ustar00rootroot00000000000000// 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-3.6.1/modules/jsUnit.js000066400000000000000000000352361320401450000155600ustar00rootroot00000000000000/* @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-3.6.1/modules/lang.js000066400000000000000000000420101320401450000152110ustar00rootroot00000000000000/* -*- mode: js; 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. // Utilities that are "meta-language" things like manipulating object props const Gi = imports._gi; 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); }; } // 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) { // Trick to apply variadic arguments to constructors -- // bind the arguments into the constructor function. let args = Array.prototype.slice.call(arguments); let curried = Function.prototype.bind.apply(metaClass, [,].concat(args)); return new curried(); } 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 newClassConstructor; if (params.Abstract) { newClassConstructor = function() { throw new TypeError('Cannot instantiate abstract class ' + name); }; } else { newClassConstructor = function() { this.__caller__ = null; return this._construct.apply(this, arguments); }; } // This is our workaround for creating a constructor with a custom // prototype. See jsapi-constructor-proxy.cpp. let newClass = __private_GjsConstructorProxy(newClassConstructor, this.constructor.prototype); newClass.__super__ = parent; // Here we have to set this property on newClassConstructor directly because // otherwise the 'prototype' property on the proxy isn't configurable newClassConstructor.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 } }); 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) { // Trick to apply variadic arguments to constructors -- // bind the arguments into the constructor function. let args = Array.prototype.slice.call(arguments); let curried = Function.prototype.bind.apply(metaInterface, [,].concat(args)); return new curried(); } 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 }); 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 || [] } }); }; cjs-3.6.1/modules/mainloop.js000066400000000000000000000054571320401450000161240ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/modules.cpp000066400000000000000000000031351320401450000161130ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/modules.gresource.xml000066400000000000000000000014211320401450000201220ustar00rootroot00000000000000 modules/tweener/equations.js modules/tweener/tweener.js modules/tweener/tweenList.js modules/overrides/GLib.js modules/overrides/Gio.js modules/overrides/GObject.js modules/overrides/Gtk.js modules/cairo.js modules/coverage.js modules/gettext.js modules/lang.js modules/_lie.js modules/mainloop.js modules/jsUnit.js modules/signals.js modules/format.js modules/package.js cjs-3.6.1/modules/modules.h000066400000000000000000000025741320401450000155660ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/overrides/000077500000000000000000000000001320401450000157375ustar00rootroot00000000000000cjs-3.6.1/modules/overrides/GLib.js000066400000000000000000000214071320401450000171160ustar00rootroot00000000000000// 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; let originalVariantClass; 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-3.6.1/modules/overrides/GObject.js000066400000000000000000000370071320401450000176210ustar00rootroot00000000000000// Copyright 2011 Jasper St. Pierre // // 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; const Gi = imports._gi; const CjsPrivate = imports.gi.CjsPrivate; let GObject; // 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 Lang.Class({ Name: 'GObjectClass', Extends: Lang.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); let propertyObj = { }; 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 Lang.Class) interfaces = interfaces.filter((iface) => !parent.implements(iface)); let gobjectInterfaces = _getGObjectInterfaces(interfaces); let propertiesArray = _propertiesAsArray(params); delete params.Properties; let newClassConstructor = Gi.register_type(parent.prototype, gtypename, gobjectInterfaces, propertiesArray); let newClass = __private_GjsConstructorProxy(newClassConstructor, this.constructor.prototype); newClass.__super__ = parent; newClass.prototype.constructor = newClass; 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 } }); interfaces.forEach((iface) => { if (iface instanceof Lang.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(params) { return this._construct.apply(this, arguments); } GObjectMeta.MetaInterface = GObjectInterface; GObjectInterface.__super__ = Lang.Interface; GObjectInterface.prototype = Object.create(Lang.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 newInterfaceConstructor = Gi.register_interface(gtypename, gobjectInterfaces, properties); let newInterface = __private_GjsConstructorProxy(newInterfaceConstructor, 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 }); return newInterface; }; GObjectInterface.prototype._init = function (params) { let signals = params.Signals; delete params.Signals; Lang.Interface.prototype._init.call(this, params); _createSignals(this.$gtype, signals); }; 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) } }, }); 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; }; // 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); }; this.Object.prototype.handler_block = function(id) { return GObject.signal_handler_block(this, id); }; this.Object.prototype.handler_unblock = function(id) { return GObject.signal_handler_unblock(this, id); }; } cjs-3.6.1/modules/overrides/Gio.js000066400000000000000000000334551320401450000170250ustar00rootroot00000000000000// 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 GObject = imports.gi.GObject; var CjsPrivate = imports.gi.CjsPrivate; var Lang = imports.lang; var Signals = imports.signals; var Gio; function _signatureLength(sig) { var counter = 0; // make it an array var signature = Array.prototype.slice.call(sig); while (signature.length) { GLib._read_single_type(sig); counter++; } return counter; } 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); else if (value instanceof XML) return constructor(value.toXMLString()); else 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 _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 _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); 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; } cjs-3.6.1/modules/overrides/Gtk.js000066400000000000000000000100121320401450000170140ustar00rootroot00000000000000// 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 Lang = imports.lang; const GObject = imports.gi.GObject; var CjsPrivate = imports.gi.CjsPrivate; let Gtk; const GtkWidgetClass = new Lang.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.Template = template; this.Children = children; this.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; }, }); function _init() { Gtk = this; 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) { GObject.Object.prototype._init.call(this, params); if (this.constructor.Template) { let children = this.constructor.Children || []; for (let child of children) this[child.replace('-', '_', 'g')] = this.get_template_child(this.constructor, child); let internalChildren = this.constructor.InternalChildren || []; for (let child of internalChildren) this['_' + child.replace('-', '_', 'g')] = this.get_template_child(this.constructor, child); } }; } cjs-3.6.1/modules/package.js000066400000000000000000000201471320401450000156720ustar00rootroot00000000000000// 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 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; 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 _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(); 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']); } 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) { let version = libs[l]; if (version != '') imports.gi.versions[l] = version; try { imports.gi[l]; } catch(e) { printerr('Unsatisfied dependency: ' + e.message); System.exit(1); } } } 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 (moduledir != pkgdatadir) { // Running from source tree, add './name' to search paths let submoduledir = GLib.build_filenamev([_base, name]); let 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-3.6.1/modules/signals.js000066400000000000000000000145261320401450000157430ustar00rootroot00000000000000/* * 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); } const WithSignals = new Lang.Interface({ Name: 'WithSignals', connect: _connect, disconnect: _disconnect, emit: _emit, disconnectAll: _disconnectAll, }); cjs-3.6.1/modules/system.cpp000066400000000000000000000141571320401450000157750ustar00rootroot00000000000000/* -*- 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 #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); bool ret; char *pointer_string; if (!gjs_parse_call_args(context, "addressOf", argv, "o", "object", &target_obj)) return false; pointer_string = g_strdup_printf("%p", target_obj.get()); ret = gjs_string_from_utf8(context, pointer_string, -1, argv.rval()); g_free(pointer_string); return ret; } 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_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(JS_GetRuntime(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::CallReceiver rec = JS::CallReceiverFromVp(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_ClearDateCaches(context); 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("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, -1, &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, JS_STUBGETTER, JS_STUBSETTER)) goto out; if (!JS_DefineProperty(context, module, "version", GJS_VERSION, GJS_MODULE_PROP_FLAGS | JSPROP_READONLY, JS_STUBGETTER, JS_STUBSETTER)) goto out; retval = true; out: g_free(program_name); return retval; } cjs-3.6.1/modules/system.h000066400000000000000000000027701320401450000154400ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/tweener/000077500000000000000000000000001320401450000154065ustar00rootroot00000000000000cjs-3.6.1/modules/tweener/equations.js000066400000000000000000000633241320401450000177640ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/tweener/tweenList.js000066400000000000000000000103601320401450000177220ustar00rootroot00000000000000/* -*- 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-3.6.1/modules/tweener/tweener.js000066400000000000000000000671721320401450000174320ustar00rootroot00000000000000/* -*- 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 _transitionList = null; 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(tweening) { 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; }; const 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, 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; 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-3.6.1/test/000077500000000000000000000000001320401450000132445ustar00rootroot00000000000000cjs-3.6.1/test/gjs-test-call-args.cpp000066400000000000000000000413441320401450000173610ustar00rootroot00000000000000#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; char *strval, *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_nonnull(objval); g_free(strval); g_free(fileval); 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) char *strval, *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_null(objval); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(unwind_free_test) char *strval, *fileval; int intval; unsigned uval; JS::RootedObject objval(cx); retval = gjs_parse_call_args(cx, "unwindFreeTest", args, "sFoiu", "strval", &strval, "fileval", &fileval, "objval", &objval, "intval", &intval, "error", &uval); g_assert_null(objval); /* Sadly, we cannot assert that strval and fileval have been freed */ 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"); typedef char *charptr; JSNATIVE_BAD_TYPE_TEST_FUNC(charptr, "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("charptrInvalidType", charptr_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::RootedObject global(fx->cx, gjs_get_import_global(fx->cx)); JS::RootedValue ignored(fx->cx); bool ok = JS::Evaluate(fx->cx, global, options, script, strlen(script), &ignored); JS_ReportPendingException(fx->cx); g_assert_null(fx->message); 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::RootedObject global(fx->cx, gjs_get_import_global(fx->cx)); JS::RootedValue ignored(fx->cx); bool ok = JS::Evaluate(fx->cx, global, options, script, strlen(script), &ignored); g_assert_false(ok); g_assert_true(JS_IsExceptionPending(fx->cx)); JS_ReportPendingException(fx->cx); g_assert_nonnull(fx->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(fx->message, expected_msg); } g_clear_pointer(&fx->message, g_free); } 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-string-type", "charptrInvalidType(1)" "//*Wrong type for i, got char??"); 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-3.6.1/test/gjs-test-coverage.cpp000066400000000000000000002651711320401450000173150ustar00rootroot00000000000000/* * Copyright © 2014 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: Sam Spilsbury */ #include #include #include #include #include #include #include #include #include #include #include #include #include "cjs/coverage.h" #include "cjs/coverage-internal.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 = "function f() { 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_internal_without_cache(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, gsize *coverage_data_length_return) { gjs_coverage_write_statistics(coverage); char *coverage_data_contents; g_file_load_contents(lcov_output, NULL /* cancellable */, &coverage_data_contents, coverage_data_length_return, NULL /* etag */, NULL /* error */); 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, gsize *coverage_data_length_return) { eval_script(context, script); return write_statistics_and_get_coverage_data(coverage, lcov_output, coverage_data_length_return); } static bool coverage_data_contains_value_for_key(const char *data, const char *key, const char *value) { const char *sf_line = line_starting_with(data, key); if (!sf_line) return false; return strncmp(&sf_line[strlen(key)], value, strlen(value)) == 0; } typedef bool (*CoverageDataMatchFunc) (const char *value, gpointer user_data); static bool coverage_data_matches_value_for_key_internal(const char *line, const char *key, CoverageDataMatchFunc match, gpointer user_data) { return (*match)(line, user_data); } static bool coverage_data_matches_value_for_key(const char *data, const char *key, CoverageDataMatchFunc match, gpointer user_data) { const char *line = line_starting_with(data, key); if (!line) return false; return coverage_data_matches_value_for_key_internal(line, key, match, user_data); } static bool coverage_data_matches_any_value_for_key(const char *data, const char *key, CoverageDataMatchFunc match, gpointer user_data) { data = line_starting_with(data, key); while (data) { if (coverage_data_matches_value_for_key_internal(data, key, match, user_data)) return true; data = line_starting_with(data + 1, key); } return false; } static bool coverage_data_matches_values_for_key(const char *data, const char *key, gsize n, CoverageDataMatchFunc match, gpointer user_data, gsize 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) { if (!coverage_data_matches_value_for_key_internal(line, key, match, (gpointer) data_iterator)) return false; line = line_starting_with(line + 1, key); --n; data_iterator += data_size; } /* If n is zero then we've found all available matches */ if (n == 0) return true; return false; } /* A simple wrapper around gjs_coverage_new */ static GjsCoverage * create_coverage_for_script(GjsContext *context, GFile *script, GFile *output_dir) { char *script_path = get_script_identifier(script); char *coverage_scripts[] = { script_path, NULL }; GjsCoverage *retval = gjs_coverage_new_internal_without_cache(coverage_scripts, context, output_dir); g_free(script_path); return retval; } static GjsCoverage * create_coverage_for_script_and_cache(GjsContext *context, GFile *cache, GFile *script, GFile *output_dir) { char *script_path = get_script_identifier(script); char *coverage_scripts[] = { script_path, NULL }; GjsCoverage *retval = gjs_coverage_new_internal_with_cache(coverage_scripts, context, output_dir, cache); g_free(script_path); return retval; } 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_internal_without_cache(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, NULL); 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, NULL); /* 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, NULL); char *expected_source_filename = get_output_path_for_script_on_disk(fixture->tmp_js_script, fixture->lcov_output_dir); g_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_internal_without_cache(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, NULL); g_log_set_always_fatal(old_flags); g_log_set_default_handler(old_log_func, NULL); g_assert(!(coverage_data_contains_value_for_key(coverage_data_contents, "SF:", "doesnotexist"))); 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 bool branch_at_line_should_be_taken(const char *line, gpointer user_data) { BranchLineData *branch_data = (BranchLineData *) 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); if (nmatches != 4) { if (errno != 0) g_error("sscanf: %s", strerror(errno)); else g_error("sscanf: only matched %i", nmatches); } /* 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); const bool hit_correct_branch_line = branch_data->expected_branch_line == line_no; const bool hit_correct_branch_id = branch_data->expected_id == branch_id; bool branch_correctly_taken_or_not_taken; switch (branch_data->taken) { case NOT_EXECUTED: branch_correctly_taken_or_not_taken = hit_count_num == -1; break; case NOT_TAKEN: branch_correctly_taken_or_not_taken = hit_count_num == 0; break; case TAKEN: branch_correctly_taken_or_not_taken = hit_count_num > 0; break; default: g_assert_not_reached(); }; return hit_correct_branch_line && hit_correct_branch_id && branch_correctly_taken_or_not_taken; } 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, NULL); const BranchLineData expected_branches[] = { { 2, 0, NOT_TAKEN }, { 2, 1, 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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, (gpointer) expected_branches, sizeof(BranchLineData))); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "BRF:", "2")); g_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, NULL); 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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, (gpointer) 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, NULL); const BranchLineData expected_branches[] = { { 3, 0, TAKEN }, { 3, 1, TAKEN }, { 3, 2, NOT_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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, (gpointer) expected_branches, sizeof(BranchLineData))); g_free(coverage_data_contents); } 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, NULL); const BranchLineData expected_branch = { 3, 0, NOT_EXECUTED }; g_assert(coverage_data_matches_any_value_for_key(coverage_data_contents, "BRDA:", branch_at_line_should_be_taken, (gpointer) &expected_branch)); g_free(coverage_data_contents); } static bool has_function_name(const char *line, gpointer user_data) { /* User data is const char ** */ const char *expected_function_name = *((const char **) user_data); /* Advance past "FN:" */ line += 3; /* Advance past the first comma */ while (*(line - 1) != ',') ++line; return strncmp(line, expected_function_name, strlen(expected_function_name)) == 0; } 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, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ const char * expected_function_names[] = { "(anonymous):2:0", "f:1:0" }; 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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_names_len, has_function_name, (gpointer) expected_function_names, sizeof(const char *))); g_free(coverage_data_contents); } static bool has_function_line(const char *line, gpointer user_data) { /* User data is const char ** */ const char *expected_function_line = *((const char **) user_data); /* Advance past "FN:" */ line += 3; return strncmp(line, expected_function_line, strlen(expected_function_line)) == 0; } 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, NULL); const char * expected_function_lines[] = { "1", "3" }; const gsize expected_function_lines_len = G_N_ELEMENTS(expected_function_lines); g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_lines_len, has_function_line, (gpointer) expected_function_lines, sizeof(const char *))); g_free(coverage_data_contents); } typedef struct _FunctionHitCountData { const char *function; unsigned int hit_count_minimum; } FunctionHitCountData; static bool hit_count_is_more_than_for_function(const char *line, gpointer user_data) { FunctionHitCountData *data = (FunctionHitCountData *) 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, "%i,%s", &hit_count, detected_function); if (nmatches != 2) { if (errno != 0) g_error("sscanf: %s", strerror(errno)); else g_error("sscanf: only matched %d", nmatches); } const bool function_name_match = g_strcmp0(data->function, detected_function) == 0; const bool hit_count_more_than = hit_count >= data->hit_count_minimum; g_free(detected_function); return function_name_match && hit_count_more_than; } /* 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, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ FunctionHitCountData expected_hit_counts[] = { { "(anonymous):6:0", 1 }, { "f:1:0", 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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, (gpointer) 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, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ FunctionHitCountData expected_hit_counts[] = { { "(anonymous):2:0", 0 }, { "(anonymous):4:0", 1 }, { "f:1:0", 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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, (gpointer) 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, NULL); /* The internal hash table is sorted in alphabetical order * so the function names need to be in this order too */ FunctionHitCountData expected_hit_counts[] = { { "(anonymous):2:0", 1 }, { "f:1:0", 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 */ g_assert(coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, (gpointer) 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, NULL); /* More than one assert per test is bad, but we are testing interlinked concepts */ g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "FNF:", "2")); g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "FNH:", "1")); g_free(coverage_data_contents); } typedef struct _LineCountIsMoreThanData { unsigned int expected_lineno; unsigned int expected_to_be_more_than; } LineCountIsMoreThanData; static bool line_hit_count_is_more_than(const char *line, gpointer user_data) { LineCountIsMoreThanData *data = (LineCountIsMoreThanData *) 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'); return data->expected_lineno == lineno && 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, NULL); LineCountIsMoreThanData data = { 1, 0 }; g_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, NULL); /* Hits on all lines, including both lines with a condition (3 and 4) */ LineCountIsMoreThanData data[] = { { 1, 0 }, { 2, 0 }, { 3, 0 }, { 4, 0 } }; g_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, NULL); /* More than one assert per test is bad, but we are testing interlinked concepts */ g_assert(coverage_data_contains_value_for_key(coverage_data_contents, "LF:", "1")); g_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, NULL); /* No files were executed, so the coverage data is empty. */ g_assert_cmpstr(coverage_data_contents, ==, ""); 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, NULL); 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_internal_without_cache(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("const 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, NULL); 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 bool check_coverage_data_for_source_file(ExpectedSourceFileCoverageData *expected, const gsize 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) { const bool line_hits_match = 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:"); const bool total_hits_match = total_hits_record[3] == expected[i].expected_lines_hit_character; const char *total_found_record = line_starting_with(section_start, "LF:"); const bool total_found_match = total_found_record[3] == expected[i].expected_lines_found_character; return line_hits_match && total_hits_match && total_found_match; } } return false; } static void test_correct_line_coverage_data_written_for_both_source_file_sectons(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, NULL); LineCountIsMoreThanData first_script_matcher = { 1, 0 }; LineCountIsMoreThanData second_script_matchers[] = { { 1, 0 }, { 2, 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:"); g_assert(check_coverage_data_for_source_file(expected, expected_len, first_sf_record)); const char *second_sf_record = line_starting_with(first_sf_record + 3, "SF:"); g_assert(check_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); } static GString * append_tuples_to_array_in_object_notation(GString *string, const char *tuple_contents_strv) { char *original_ptr = (char *) tuple_contents_strv; char *expected_tuple_contents = NULL; while ((expected_tuple_contents = strsep((char **) &tuple_contents_strv, ";")) != NULL) { if (!strlen(expected_tuple_contents)) continue; if (expected_tuple_contents != original_ptr) g_string_append_printf(string, ","); g_string_append_printf(string, "{%s}", expected_tuple_contents); } return string; } static GString * format_expected_cache_object_notation(const char *mtimes, const char *hash, GFile *script, const char *expected_executable_lines_array, const char *expected_branches, const char *expected_functions) { char *script_name = get_script_identifier(script); GString *string = g_string_new(""); g_string_append_printf(string, "{\"%s\":{\"mtime\":%s,\"checksum\":%s,\"lines\":[%s],\"branches\":[", script_name, mtimes, hash, expected_executable_lines_array); g_free(script_name); append_tuples_to_array_in_object_notation(string, expected_branches); g_string_append_printf(string, "],\"functions\":["); append_tuples_to_array_in_object_notation(string, expected_functions); g_string_append_printf(string, "]}}"); return string; } typedef struct _GjsCoverageCacheObjectNotationTestTableData { const char *test_name; const char *script; const char *uri; const char *expected_executable_lines; const char *expected_branches; const char *expected_functions; } GjsCoverageCacheObjectNotationTableTestData; static GBytes * serialize_ast_to_bytes(GjsCoverage *coverage, const char **coverage_paths) { return gjs_serialize_statistics(coverage); } static char * serialize_ast_to_object_notation(GjsCoverage *coverage, const char **coverage_paths) { /* Unfortunately, we need to pass in this paramater here since * the len parameter is not allow-none. * * The caller doesn't need to know about the length of the * data since it is only used for strcmp and the data is * NUL-terminated anyway. */ gsize len = 0; return (char *)g_bytes_unref_to_data(serialize_ast_to_bytes(coverage, coverage_paths), &len); } static char * eval_file_for_ast_in_object_notation(GjsContext *context, GjsCoverage *coverage, GFile *script) { bool success = eval_script(context, script); g_assert_true(success); char *filename = g_file_get_path(script); const gchar *coverage_paths[] = { filename, NULL }; char *retval = serialize_ast_to_object_notation(coverage, coverage_paths); g_free(filename); return retval; } static void test_coverage_cache_data_in_expected_format(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GjsCoverageCacheObjectNotationTableTestData *table_data = (GjsCoverageCacheObjectNotationTableTestData *) user_data; replace_file(fixture->tmp_js_script, table_data->script); char *cache_in_object_notation = eval_file_for_ast_in_object_notation(fixture->context, fixture->coverage, fixture->tmp_js_script); g_assert(cache_in_object_notation != NULL); /* Sleep for a little while to make sure that the new file has a * different mtime */ sleep(1); GTimeVal mtime; bool successfully_got_mtime = gjs_get_file_mtime(fixture->tmp_js_script, &mtime); g_assert_true(successfully_got_mtime); char *mtime_string = g_strdup_printf("[%li,%li]", mtime.tv_sec, mtime.tv_usec); GString *expected_cache_object_notation = format_expected_cache_object_notation(mtime_string, "null", fixture->tmp_js_script, table_data->expected_executable_lines, table_data->expected_branches, table_data->expected_functions); g_assert_cmpstr(cache_in_object_notation, ==, expected_cache_object_notation->str); g_string_free(expected_cache_object_notation, true); g_free(cache_in_object_notation); g_free(mtime_string); } static void test_coverage_cache_data_in_expected_format_resource(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GjsCoverageCacheObjectNotationTableTestData *table_data = (GjsCoverageCacheObjectNotationTableTestData *) user_data; GFile *resource = g_file_new_for_uri(table_data->uri); char *hash_string_no_quotes = gjs_get_file_checksum(resource); char *hash_string = g_strdup_printf("\"%s\"", hash_string_no_quotes); g_free(hash_string_no_quotes); GString *expected_cache_object_notation = format_expected_cache_object_notation("null", hash_string, resource, table_data->expected_executable_lines, table_data->expected_branches, table_data->expected_functions); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script(fixture->context, resource, fixture->tmp_output_dir); char *cache_in_object_notation = eval_file_for_ast_in_object_notation(fixture->context, fixture->coverage, resource); g_object_unref(resource); g_assert_cmpstr(cache_in_object_notation, ==, expected_cache_object_notation->str); g_string_free(expected_cache_object_notation, true); g_free(cache_in_object_notation); g_free(hash_string); } static char * generate_coverage_compartment_verify_script(GFile *coverage_script, const char *user_script) { char *coverage_script_filename = g_file_get_path(coverage_script); char *retval = g_strdup_printf("const covered_script_filename = '%s';\n" "function assertEquals(lhs, rhs) {\n" " if (lhs !== rhs)\n" " throw new Error('Assertion failure');\n" "}\n" "function assertArrayEquals(lhs, rhs) {\n" " assertEquals(lhs.length, rhs.length);\n" " for (let i = 0; i < lhs.length; i++)\n" " assertEquals(lhs[i], rhs[i]);\n" "}\n" "\n" "%s", coverage_script_filename, user_script); g_free(coverage_script_filename); return retval; } typedef struct _GjsCoverageCacheJSObjectTableTestData { const char *test_name; const char *script; const char *verify_js_script; } GjsCoverageCacheJSObjectTableTestData; static void test_coverage_cache_as_js_object_has_expected_properties(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GjsCoverageCacheJSObjectTableTestData *table_data = (GjsCoverageCacheJSObjectTableTestData *) user_data; replace_file(fixture->tmp_js_script, table_data->script); eval_script(fixture->context, fixture->tmp_js_script); char *script_filename = g_file_get_path(fixture->tmp_js_script); const gchar *coverage_paths[] = { script_filename, NULL }; GBytes *cache = serialize_ast_to_bytes(fixture->coverage, coverage_paths); JS::RootedString cache_results(JS_GetRuntime((JSContext *) gjs_context_get_native_context(fixture->context)), gjs_deserialize_cache_to_object(fixture->coverage, cache)); JS::RootedValue cache_result_value(JS_GetRuntime((JSContext *) gjs_context_get_native_context(fixture->context)), JS::StringValue(cache_results)); gjs_inject_value_into_coverage_compartment(fixture->coverage, cache_result_value, "coverage_cache"); char *verify_script_complete = generate_coverage_compartment_verify_script(fixture->tmp_js_script, table_data->verify_js_script); gjs_run_script_in_coverage_compartment(fixture->coverage, verify_script_complete); g_free(verify_script_complete); g_free(script_filename); g_bytes_unref(cache); } typedef struct _GjsCoverageCacheEqualResultsTableTestData { const char *test_name; const char *script; } GjsCoverageCacheEqualResultsTableTestData; static GFile * get_coverage_tmp_cache(void) { GFileIOStream *stream; GError *error = NULL; GFile *cache_file = g_file_new_tmp("gjs-coverage-cache-XXXXXX", &stream, &error); g_assert_no_error(error); g_assert_nonnull(cache_file); g_object_unref(stream); return cache_file; } static GFile * write_cache_to_temporary_file(GBytes *cache) { GFile *temporary_file = get_coverage_tmp_cache(); if (!gjs_write_cache_file(temporary_file, cache)) { g_object_unref(temporary_file); return NULL; } return temporary_file; } static GFile * serialize_ast_to_cache_in_temporary_file(GjsCoverage *coverage, const char **coverage_paths) { GBytes *cache = serialize_ast_to_bytes(coverage, coverage_paths); GFile *cache_file = write_cache_to_temporary_file(cache); g_bytes_unref(cache); return cache_file; } static void test_coverage_cache_equal_results_to_reflect_parse(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GjsCoverageCacheEqualResultsTableTestData *equal_results_data = (GjsCoverageCacheEqualResultsTableTestData *) user_data; replace_file(fixture->tmp_js_script, equal_results_data->script); char *tmp_js_script_filename = g_file_get_path(fixture->tmp_js_script); const gchar *coverage_paths[] = { tmp_js_script_filename, NULL }; char *coverage_data_contents_no_cache = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output, NULL); GFile *cache_file = serialize_ast_to_cache_in_temporary_file(fixture->coverage, coverage_paths); g_assert_nonnull(cache_file); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); g_object_unref(cache_file); /* Overwrite tracefile with nothing and start over */ replace_file(fixture->lcov_output, ""); char *coverage_data_contents_cached = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output, NULL); g_assert_cmpstr(coverage_data_contents_cached, ==, coverage_data_contents_no_cache); g_free(coverage_data_contents_cached); g_free(coverage_data_contents_no_cache); g_free(tmp_js_script_filename); } static GFile * eval_file_for_tmp_ast_cache(GjsContext *context, GjsCoverage *coverage, GFile *script) { bool success = eval_script(context, script); g_assert_true(success); char *filename = g_file_get_path(script); const gchar *coverage_paths[] = { filename, NULL }; GFile *retval = serialize_ast_to_cache_in_temporary_file(coverage, coverage_paths); g_free(filename); return retval; } /* Effectively, the results should be what we expect even though * we overwrote the original script after getting coverage and * fetching the cache */ static void test_coverage_cache_invalidation(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GFile *cache_file = eval_file_for_tmp_ast_cache(fixture->context, fixture->coverage, fixture->tmp_js_script); /* Sleep for a little while to make sure that the new file has a * different mtime */ sleep(1); /* Overwrite tracefile with nothing */ replace_file(fixture->lcov_output, ""); /* Write a new script into the temporary js file, which will be * completely different to the original script that was there */ replace_file(fixture->tmp_js_script, "let i = 0;\n" "let j = 0;\n"); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); g_object_unref(cache_file); gsize coverage_data_len = 0; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output, &coverage_data_len); LineCountIsMoreThanData matchers[] = { { 1, 0 }, { 2, 0 } }; char *script_output_path = get_output_path_for_script_on_disk(fixture->tmp_js_script, fixture->lcov_output_dir); ExpectedSourceFileCoverageData expected[] = { { script_output_path, matchers, 2, '2', '2' } }; const gsize expected_len = G_N_ELEMENTS(expected); const char *record = line_starting_with(coverage_data_contents, "SF:"); g_assert(check_coverage_data_for_source_file(expected, expected_len, record)); g_free(script_output_path); g_free(coverage_data_contents); } static void unload_resource(GResource *resource) { g_resources_unregister(resource); g_resource_unref(resource); } static GResource * load_resource_from_builddir(const char *name) { char *resource_path = g_build_filename(GJS_TOP_BUILDDIR, name, NULL); GError *error = NULL; GResource *resource = g_resource_load(resource_path, &error); g_assert_no_error(error); g_resources_register(resource); g_free(resource_path); return resource; } /* Load first resource, then unload and load second resource. Both have * the same path, but different contents */ static void test_coverage_cache_invalidation_resource(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GFile *mock_resource = g_file_new_for_uri("resource:///org/cinnamon/cjs/mock/cache/resource.js"); /* Load the resource archive and register it */ GResource *first_resource = load_resource_from_builddir("mock-cache-invalidation-before.gresource"); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script(fixture->context, mock_resource, fixture->lcov_output_dir); GFile *cache_file = eval_file_for_tmp_ast_cache(fixture->context, fixture->coverage, mock_resource); /* Load the "after" resource, but have the exact same coverage paths */ unload_resource(first_resource); GResource *second_resource = load_resource_from_builddir("mock-cache-invalidation-after.gresource"); /* Overwrite tracefile with nothing */ replace_file(fixture->lcov_output, ""); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, mock_resource, fixture->lcov_output_dir); g_object_unref(cache_file); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, mock_resource, fixture->lcov_output, NULL); /* Don't need this anymore */ g_object_unref(mock_resource); unload_resource(second_resource); /* Now assert that the coverage file has executable lines in * the places that we expect them to be */ LineCountIsMoreThanData matchers[] = { { 1, 0 }, { 2, 0 } }; GFile *output_script = g_file_resolve_relative_path(fixture->lcov_output_dir, "org/cinnamon/cjs/mock/cache/resource.js"); char *script_output_path = g_file_get_path(output_script); g_object_unref(output_script); ExpectedSourceFileCoverageData expected[] = { { script_output_path, matchers, 2, '2', '2' } }; const gsize expected_len = G_N_ELEMENTS(expected); const char *record = line_starting_with(coverage_data_contents, "SF:"); g_assert(check_coverage_data_for_source_file(expected, expected_len, record)); g_free(script_output_path); g_free(coverage_data_contents); } static void test_coverage_cache_file_written_when_no_cache_exists(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GFile *cache_file = get_coverage_tmp_cache(); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); /* We need to execute the script now in order for a cache entry * to be created, since unexecuted scripts are not counted as * part of the coverage report. */ bool success = eval_script(fixture->context, fixture->tmp_js_script); g_assert_true(success); gjs_coverage_write_statistics(fixture->coverage); g_assert_true(g_file_query_exists(cache_file, NULL)); g_object_unref(cache_file); } static GTimeVal eval_script_for_cache_mtime(GjsContext *context, GjsCoverage *coverage, GFile *cache_file, GFile *script) { bool success = eval_script(context, script); g_assert_true(success); gjs_coverage_write_statistics(coverage); GTimeVal mtime; bool successfully_got_mtime = gjs_get_file_mtime(cache_file, &mtime); g_assert_true(successfully_got_mtime); return mtime; } static void test_coverage_cache_updated_when_cache_stale(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GFile *cache_file = get_coverage_tmp_cache(); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); GTimeVal first_cache_mtime = eval_script_for_cache_mtime(fixture->context, fixture->coverage, cache_file, fixture->tmp_js_script); /* Sleep for a little while to make sure that the new file has a * different mtime */ sleep(1); /* Write a new script into the temporary js file, which will be * completely different to the original script that was there */ replace_file(fixture->tmp_js_script, "let i = 0;\n" "let j = 0;\n"); /* Re-create coverage object, covering new script */ g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); /* Run the script again, which will cause an attempt * to look up the AST data. Upon writing the statistics * again, the cache should have been missed some of the time * so the second mtime will be greater than the first */ GTimeVal second_cache_mtime = eval_script_for_cache_mtime(fixture->context, fixture->coverage, cache_file, fixture->tmp_js_script); const bool seconds_different = (first_cache_mtime.tv_sec != second_cache_mtime.tv_sec); const bool microseconds_different = (first_cache_mtime.tv_usec != second_cache_mtime.tv_usec); g_assert_true(seconds_different || microseconds_different); g_object_unref(cache_file); } static void test_coverage_cache_not_updated_on_full_hits(gpointer fixture_data, gconstpointer user_data) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; GFile *cache_file = get_coverage_tmp_cache(); g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); GTimeVal first_cache_mtime = eval_script_for_cache_mtime(fixture->context, fixture->coverage, cache_file, fixture->tmp_js_script); /* Re-create coverage object, covering same script */ g_clear_object(&fixture->coverage); fixture->coverage = create_coverage_for_script_and_cache(fixture->context, cache_file, fixture->tmp_js_script, fixture->lcov_output_dir); /* Run the script again, which will cause an attempt * to look up the AST data. Upon writing the statistics * again, the cache should have been hit of the time * so the second mtime will be the same as the first */ GTimeVal second_cache_mtime = eval_script_for_cache_mtime(fixture->context, fixture->coverage, cache_file, fixture->tmp_js_script); g_assert_cmpint(first_cache_mtime.tv_sec, ==, second_cache_mtime.tv_sec); g_assert_cmpint(first_cache_mtime.tv_usec, ==, second_cache_mtime.tv_usec); g_object_unref(cache_file); } 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); } /* All table driven tests must be binary compatible with at * least this header */ typedef struct _TestTableDataHeader { const char *test_name; } TestTableDataHeader; static void add_table_driven_test_for_fixture(const char *name, FixturedTest *fixture, GTestFixtureFunc test_func, gsize table_entry_size, gsize n_table_entries, const TestTableDataHeader *test_table) { const char *test_table_ptr = (const char *)test_table; gsize test_table_index; for (test_table_index = 0; test_table_index < n_table_entries; ++test_table_index, test_table_ptr += table_entry_size) { const TestTableDataHeader *header = reinterpret_cast(test_table_ptr); gchar *test_name_for_table_index = g_strdup_printf("%s/%s", name, header->test_name); g_test_add_vtable(test_name_for_table_index, fixture->fixture_size, test_table_ptr, fixture->set_up, test_func, fixture->tear_down); g_free(test_name_for_table_index); } } 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_sectons, NULL); /* This must be static, because g_test_add_vtable does not copy it */ static GjsCoverageCacheObjectNotationTableTestData data_in_expected_format_table[] = { { "simple_executable_lines", "let i = 0;\n", "resource://org/cinnamon/cjs/mock/test/gjs-test-coverage/cache_notation/simple_executable_lines.js", "1", "", "" }, { "simple_branch", "let i = 0;\n" "if (i) {\n" " i = 1;\n" "} else {\n" " i = 2;\n" "}\n", "resource://org/cinnamon/cjs/mock/test/gjs-test-coverage/cache_notation/simple_branch.js", "1,2,3,5", "\"point\":2,\"exits\":[3,5]", "" }, { "simple_function", "function f() {\n" "}\n", "resource://org/cinnamon/cjs/mock/test/gjs-test-coverage/cache_notation/simple_function.js", "1,2", "", "\"key\":\"f:1:0\",\"line\":1" } }; add_table_driven_test_for_fixture("/gjs/coverage/cache/data_format", &coverage_fixture, test_coverage_cache_data_in_expected_format, sizeof(GjsCoverageCacheObjectNotationTableTestData), G_N_ELEMENTS(data_in_expected_format_table), (const TestTableDataHeader *) data_in_expected_format_table); add_table_driven_test_for_fixture("/gjs/coverage/cache/data_format_resource", &coverage_fixture, test_coverage_cache_data_in_expected_format_resource, sizeof(GjsCoverageCacheObjectNotationTableTestData), G_N_ELEMENTS(data_in_expected_format_table), (const TestTableDataHeader *) data_in_expected_format_table); static GjsCoverageCacheJSObjectTableTestData object_has_expected_properties_table[] = { { "simple_executable_lines", "let i = 0;\n", "assertArrayEquals(JSON.parse(coverage_cache)[covered_script_filename].lines, [1]);\n" }, { "simple_branch", "let i = 0;\n" "if (i) {\n" " i = 1;\n" "} else {\n" " i = 2;\n" "}\n", "assertEquals(2, JSON.parse(coverage_cache)[covered_script_filename].branches[0].point);\n" "assertArrayEquals([3, 5], JSON.parse(coverage_cache)[covered_script_filename].branches[0].exits);\n" }, { "simple_function", "function f() {\n" "}\n", "assertEquals('f:1:0', JSON.parse(coverage_cache)[covered_script_filename].functions[0].key);\n" } }; add_table_driven_test_for_fixture("/gjs/coverage/cache/object_props", &coverage_fixture, test_coverage_cache_as_js_object_has_expected_properties, sizeof(GjsCoverageCacheJSObjectTableTestData), G_N_ELEMENTS(object_has_expected_properties_table), (const TestTableDataHeader *) object_has_expected_properties_table); static GjsCoverageCacheEqualResultsTableTestData equal_results_table[] = { { "simple_executable_lines", "let i = 0;\n" "let j = 1;\n" }, { "simple_branch", "let i = 0;\n" "if (i) {\n" " i = 1;\n" "} else {\n" " i = 2;\n" "}\n" }, { "simple_function", "function f() {\n" "}\n" } }; add_table_driven_test_for_fixture("/gjs/coverage/cache/equal/executable_lines", &coverage_fixture, test_coverage_cache_equal_results_to_reflect_parse, sizeof(GjsCoverageCacheEqualResultsTableTestData), G_N_ELEMENTS(equal_results_table), (const TestTableDataHeader *) equal_results_table); add_test_for_fixture("/gjs/coverage/cache/invalidation", &coverage_fixture, test_coverage_cache_invalidation, NULL); add_test_for_fixture("/gjs/coverage/cache/invalidation_resource", &coverage_fixture, test_coverage_cache_invalidation_resource, NULL); add_test_for_fixture("/gjs/coverage/cache/file_written", &coverage_fixture, test_coverage_cache_file_written_when_no_cache_exists, NULL); add_test_for_fixture("/gjs/coverage/cache/no_update_on_full_hits", &coverage_fixture, test_coverage_cache_not_updated_on_full_hits, NULL); add_test_for_fixture("/gjs/coverage/cache/update_on_misses", &coverage_fixture, test_coverage_cache_updated_when_cache_stale, NULL); } cjs-3.6.1/test/gjs-test-coverage/000077500000000000000000000000001320401450000165755ustar00rootroot00000000000000cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/000077500000000000000000000000001320401450000224015ustar00rootroot00000000000000cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/after/000077500000000000000000000000001320401450000235025ustar00rootroot00000000000000cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/after/mock-js-resource-cache-after.gresource.xml000066400000000000000000000004241320401450000335510ustar00rootroot00000000000000 resource.js cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/after/resource.js000066400000000000000000000000261320401450000256650ustar00rootroot00000000000000let i = 0; let j = 1; cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/before/000077500000000000000000000000001320401450000236435ustar00rootroot00000000000000mock-js-resource-cache-before.gresource.xml000066400000000000000000000004411320401450000337730ustar00rootroot00000000000000cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/before resource.js cjs-3.6.1/test/gjs-test-coverage/cache_invalidation/before/resource.js000066400000000000000000000000211320401450000260210ustar00rootroot00000000000000function f() { } cjs-3.6.1/test/gjs-test-coverage/cache_notation/000077500000000000000000000000001320401450000215535ustar00rootroot00000000000000cjs-3.6.1/test/gjs-test-coverage/cache_notation/simple_branch.js000066400000000000000000000000651320401450000247200ustar00rootroot00000000000000let i = 0; if (i) { i = 1; } else { i = 2; } cjs-3.6.1/test/gjs-test-coverage/cache_notation/simple_executable_lines.js000066400000000000000000000000131320401450000267670ustar00rootroot00000000000000let i = 0; cjs-3.6.1/test/gjs-test-coverage/cache_notation/simple_function.js000066400000000000000000000000211320401450000253000ustar00rootroot00000000000000function f() { } cjs-3.6.1/test/gjs-test-coverage/loadedJSFromResource.js000066400000000000000000000000341320401450000231510ustar00rootroot00000000000000function mock_function() {} cjs-3.6.1/test/gjs-test-rooting.cpp000066400000000000000000000207371320401450000172000ustar00rootroot00000000000000#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 JSClass test_obj_class = { "TestObj", JSCLASS_HAS_PRIVATE, NULL, /* addProperty */ NULL, /* deleteProperty */ NULL, /* getProperty */ NULL, /* setProperty */ NULL, /* enumerate */ NULL, /* resolve */ NULL, /* convert */ test_obj_finalize }; 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(JSRuntime *rt, 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(JS_GetRuntime(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(JS_GetRuntime(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 teardown_context_already_destroyed(GjsRootingFixture *fx, gconstpointer unused) { gjs_unit_test_teardown_context_already_destroyed(PARENT(fx)); } 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, NULL, setup, f, \ teardown_context_already_destroyed); 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-3.6.1/test/gjs-test-utils.cpp000066400000000000000000000124021320401450000166450ustar00rootroot00000000000000/* -*- 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" static void test_error_reporter(JSContext *context, const char *message, JSErrorReport *report) { GjsContext *gjs_context = gjs_context_get_current(); GjsUnitTestFixture *fx = (GjsUnitTestFixture *) g_object_get_data(G_OBJECT(gjs_context), "test fixture"); g_free(fx->message); fx->message = g_strdup(message); } 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); /* This is for shoving private data into the error reporter callback */ g_object_set_data(G_OBJECT(fx->gjs_context), "test fixture", fx); JS_SetErrorReporter(JS_GetRuntime(fx->cx), test_error_reporter); 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) { JS_LeaveCompartment(fx->cx, fx->compartment); JS_EndRequest(fx->cx); g_object_unref(fx->gjs_context); } void gjs_unit_test_teardown_context_already_destroyed(GjsUnitTestFixture *fx) { if (fx->message != NULL) g_printerr("**\n%s\n", fx->message); g_free(fx->message); } void gjs_unit_test_fixture_teardown(GjsUnitTestFixture *fx, gconstpointer unused) { gjs_unit_test_destroy_context(fx); gjs_unit_test_teardown_context_already_destroyed(fx); } /* 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-3.6.1/test/gjs-test-utils.h000066400000000000000000000032701320401450000163150ustar00rootroot00000000000000/* * Copyright © 2013 Endless Mobile, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 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; char *message; /* Thrown exception message */ }; void gjs_unit_test_fixture_setup(GjsUnitTestFixture *fx, gconstpointer unused); void gjs_unit_test_destroy_context(GjsUnitTestFixture *fx); void gjs_unit_test_teardown_context_already_destroyed(GjsUnitTestFixture *fx); void gjs_unit_test_fixture_teardown(GjsUnitTestFixture *fx, gconstpointer unused); 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-3.6.1/test/gjs-tests.cpp000066400000000000000000000334351320401450000157030ustar00rootroot00000000000000/* -*- 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 Lang = imports.lang; \ const GObject = imports.gi.GObject; \ \ const FooBar = new Lang.Class({ \ Name: '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) { char *utf8_result; JS::RootedValue js_string(fx->cx); g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, -1, &js_string)); g_assert(js_string.isString()); g_assert(gjs_string_to_utf8(fx->cx, js_string, &utf8_result)); g_assert_cmpstr(VALID_UTF8_STRING, ==, utf8_result); g_free(utf8_result); } static void gjstest_test_func_gjs_jsapi_util_error_throw(GjsUnitTestFixture *fx, gconstpointer unused) { JS::RootedValue exc(fx->cx), value(fx->cx); char *s = NULL; /* 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()); gjs_string_to_utf8(fx->cx, value, &s); g_assert_nonnull(s); g_assert_cmpstr(s, ==, "This is an exception 42"); JS_free(fx->cx, s); /* 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_char16_data(GjsUnitTestFixture *fx, gconstpointer unused) { char16_t *chars; size_t len; JS::RootedValue v_string(fx->cx); g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, -1, &v_string)); g_assert_true(gjs_string_get_char16_data(fx->cx, v_string, &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 */ v_string.setString(JS_NewStringCopyZ(fx->cx, "abcd")); g_assert_true(gjs_string_get_char16_data(fx->cx, v_string, &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::RootedValue v_string(fx->cx); g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, -1, &v_string)); g_assert_true(gjs_string_to_ucs4(fx->cx, v_string, &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 */ v_string.setString(JS_NewStringCopyZ(fx->cx, "abcd")); g_assert_true(gjs_string_to_ucs4(fx->cx, v_string, &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, -1, &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); } 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); 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("/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/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-3.6.1/test/mock-js-resources.gresource.xml000066400000000000000000000006401320401450000213360ustar00rootroot00000000000000 test/gjs-test-coverage/loadedJSFromResource.js test/gjs-test-coverage/cache_notation/simple_executable_lines.js test/gjs-test-coverage/cache_notation/simple_branch.js test/gjs-test-coverage/cache_notation/simple_function.js cjs-3.6.1/test/run-test000077500000000000000000000002441320401450000147530ustar00rootroot00000000000000#!/bin/sh -e # Run a GTester binary with TAP output if test -z "$1"; then echo "Need a test-binary filename!" exit 1 fi "$1" --tap --keep-going --verbose cjs-3.6.1/test/test-bus.conf000066400000000000000000000036711320401450000156700ustar00rootroot00000000000000 session unix:tmpdir=/tmp 1000000000 1000000000 1000000000 120000 240000 100000 10000 100000 10000 50000 50000 50000 300000 cjs-3.6.1/test/travis-ci.sh000077500000000000000000000152501320401450000155070ustar00rootroot00000000000000#!/bin/bash -e function do_Install_Base_Dependencies(){ echo echo '-- Installing Base Dependencies --' if [[ $BASE == "ubuntu" ]]; then apt-get update -qq # Base dependencies apt-get -y -qq install build-essential git clang patch python-dev \ autotools-dev autoconf gettext pkgconf autopoint yelp-tools \ docbook docbook-xsl libtext-csv-perl \ zlib1g-dev \ libtool libicu-dev libnspr4-dev \ policykit-1 \ apt-file > /dev/null apt-file update elif [[ $BASE == "fedora" ]]; then dnf -y -q upgrade # Base dependencies dnf -y -q install @c-development @development-tools redhat-rpm-config gnome-common python-devel \ pygobject2 dbus-python perl-Text-CSV perl-XML-Parser gettext-devel gtk-doc ninja-build \ zlib-devel libffi-devel \ libtool libicu-devel nspr-devel else echo echo '-- Error: invalid BASE code --' exit 1 fi } function do_Install_Dependencies(){ echo echo '-- Installing Dependencies --' if [[ $BASE == "ubuntu" ]]; then # Testing dependencies apt-get -y -qq install libgtk-3-dev gir1.2-gtk-3.0 xvfb gnome-desktop-testing dbus-x11 dbus \ libreadline6 libreadline6-dev > /dev/null elif [[ $BASE == "fedora" ]]; then # Testing dependencies dnf -y -q install gtk3 gtk3-devel gobject-introspection Xvfb gnome-desktop-testing dbus-x11 dbus \ cairo intltool libxslt bison nspr zlib python3-devel dbus-glib libicu libffi pcre libxml2 libxslt libtool flex \ cairo-devel zlib-devel libffi-devel pcre-devel libxml2-devel libxslt-devel \ libedit libedit-devel fi } function do_Set_Env(){ echo echo '-- Set Environment --' #Save cache on host mkdir -p /cwd/.cache export XDG_CACHE_HOME=/cwd/.cache if [[ -z $DISPLAY ]]; then export DISPLAY=":0" fi echo '-- Done --' } function do_Patch_JHBuild(){ echo echo '-- Patching JHBuild --' # Create and apply a patch cd jhbuild patch -p1 <> ~/.config/jhbuildrc module_autogenargs['gjs'] = "$autogenargs" module_makeargs['gjs'] = '-s' skip = ['gettext', 'yelp-xsl', 'yelp-tools', 'gtk-doc'] use_local_modulesets = True EOFILE echo '-- Done --' } function do_Build_JHBuild(){ echo echo '-- Building JHBuild --' # Build JHBuild cd jhbuild git log --pretty=format:"%h %cd %s" -1 echo ./autogen.sh make -sj2 make install PATH=$PATH:~/.local/bin if [[ $1 == "RESET" ]]; then git reset --hard HEAD fi } function do_Build_Mozilla(){ echo echo '-- Building Mozilla SpiderMonkey --' # Build Mozilla Stuff if [[ -n "$SHELL" ]]; then export SHELL=/bin/bash fi #TODO Fix this upstream #STOP! /root/jhbuild/checkout/mozjs-38.0.0/js/src/configure.in has changed, and your configure is out of date. jhbuild update mozjs38 touch ~/jhbuild/checkout/mozjs-38.0.0/js/src/configure jhbuild build mozjs38 } function do_Build_Package_Dependencies(){ echo echo "-- Building Dependencies for $1 --" jhbuild list $1 # Build package dependencies if [[ $BASE == "ubuntu" ]]; then jhbuild sysdeps --install $1 fi jhbuild build $(jhbuild list $1 | sed '$d') } function do_Save_Files(){ echo echo '-- Saving build files --' mkdir -p /cwd/SAVED/$OS cp -r ~/jhbuild /cwd/SAVED/$OS/jhbuild cp -r ~/.local /cwd/SAVED/$OS/.local echo '-- Done --' } function do_Get_Files(){ echo echo '-- Restoring build files --' cp -r /cwd/SAVED/$OS/jhbuild ~/jhbuild cp -r /cwd/SAVED/$OS/.local ~/.local echo '-- Done --' } function do_Show_Compiler(){ if [[ ! -z $CC ]]; then echo echo '-- Compiler in use --' $CC --version fi } # ----------- GJS ----------- cd /cwd # Show some environment info echo echo '-- Environment --' echo "Running on: $BASE $OS" echo "Doing: $1" if [[ $1 == "BUILD_MOZ" ]]; then do_Install_Base_Dependencies do_Show_Compiler do_Patch_JHBuild do_Build_JHBuild RESET do_Build_Mozilla do_Save_Files elif [[ $1 == "GET_FILES" ]]; then do_Get_Files elif [[ $1 == "GJS" ]]; then do_Install_Base_Dependencies do_Install_Dependencies do_Set_Env do_Show_Compiler do_Patch_JHBuild do_Build_JHBuild do_Configure_JHBuild do_Build_Package_Dependencies gjs # Build and test the latest commit (merged or from a PR) of Javascript Bindings for GNOME echo echo '-- gjs build --' cp -r ../gjs ~/jhbuild/checkout/gjs cd ~/jhbuild/checkout/gjs git log --pretty=format:"%h %cd %s" -1 echo jhbuild make --check # Extra testing echo echo '-- Installed GJS tests --' xvfb-run jhbuild run dbus-run-session -- gnome-desktop-testing-runner gjs else echo echo '-- NOTHING TO DO --' exit 1 fi # Done echo echo '-- DONE --' cjs-3.6.1/util/000077500000000000000000000000001320401450000132425ustar00rootroot00000000000000cjs-3.6.1/util/error.cpp000066400000000000000000000024441320401450000151030ustar00rootroot00000000000000/* -*- 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 "error.h" GQuark gjs_error_quark (void) { return g_quark_from_static_string ("gjs-error-quark"); } cjs-3.6.1/util/error.h000066400000000000000000000027401320401450000145470ustar00rootroot00000000000000/* -*- 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; G_END_DECLS #endif /* __GJS_UTIL_ERROR_H__ */ cjs-3.6.1/util/glib.cpp000066400000000000000000000040371320401450000146670ustar00rootroot00000000000000/* -*- 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-3.6.1/util/glib.h000066400000000000000000000026421320401450000143340ustar00rootroot00000000000000/* -*- 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-3.6.1/util/hash-x32.cpp000066400000000000000000000045231320401450000153070ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 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 "hash-x32.h" /* Note: Not actually tested on x32 */ #define HASH_GSIZE_FITS_POINTER (sizeof(gsize) == sizeof(gpointer)) GHashTable * gjs_hash_table_new_for_gsize (GDestroyNotify value_destroy) { if (HASH_GSIZE_FITS_POINTER) { return g_hash_table_new_full (NULL, NULL, NULL, value_destroy); } else { return g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, value_destroy); } } void gjs_hash_table_for_gsize_insert (GHashTable *table, gsize key, gpointer value) { if (HASH_GSIZE_FITS_POINTER) { g_hash_table_insert (table, (gpointer)key, value); } else { guint64 *keycopy = g_new (guint64, 1); *keycopy = (guint64) key; g_hash_table_insert (table, keycopy, value); } } void gjs_hash_table_for_gsize_remove (GHashTable *table, gsize key) { if (HASH_GSIZE_FITS_POINTER) g_hash_table_remove (table, (gpointer)key); else g_hash_table_remove (table, &key); } gpointer gjs_hash_table_for_gsize_lookup (GHashTable *table, gsize key) { if (HASH_GSIZE_FITS_POINTER) return g_hash_table_lookup (table, (gpointer)key); else return g_hash_table_lookup (table, &key); } cjs-3.6.1/util/hash-x32.h000066400000000000000000000034341320401450000147540ustar00rootroot00000000000000/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 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_UTIL_HASH_X32_H__ #define __GJS_UTIL_HASH_X32_H__ #include G_BEGIN_DECLS /* Hash table that operates on gsize; on every architecture except x32, * sizeof(gsize) == sizeof(gpointer), and so we can just use it as a * hash key directly. But on x32, we have to fall back to malloc(). */ GHashTable *gjs_hash_table_new_for_gsize (GDestroyNotify value_destroy); void gjs_hash_table_for_gsize_insert (GHashTable *table, gsize key, gpointer value); void gjs_hash_table_for_gsize_remove (GHashTable *table, gsize key); gpointer gjs_hash_table_for_gsize_lookup (GHashTable *table, gsize key); G_END_DECLS #endif cjs-3.6.1/util/log.cpp000066400000000000000000000167101320401450000145340ustar00rootroot00000000000000/* -*- 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 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 (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_DATABASE: prefix = "JS DB"; break; case GJS_DEBUG_RESULTSET: prefix = "JS RS"; break; case GJS_DEBUG_WEAK_HASH: prefix = "JS WEAK"; break; case GJS_DEBUG_MAINLOOP: prefix = "JS MAINLOOP"; break; case GJS_DEBUG_PROPS: prefix = "JS PROPS"; break; case GJS_DEBUG_SCOPE: prefix = "JS SCOPE"; break; case GJS_DEBUG_HTTP: prefix = "JS HTTP"; break; case GJS_DEBUG_BYTE_ARRAY: prefix = "JS BYTE ARRAY"; break; case GJS_DEBUG_GERROR: prefix = "JS G ERR"; break; case GJS_DEBUG_PROXY: prefix = "JS CPROXY"; 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; } write_to_stream(logfp, prefix, s); g_free(s); } cjs-3.6.1/util/log.h000066400000000000000000000115221320401450000141750ustar00rootroot00000000000000/* -*- 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_DATABASE, GJS_DEBUG_RESULTSET, GJS_DEBUG_WEAK_HASH, GJS_DEBUG_MAINLOOP, GJS_DEBUG_PROPS, GJS_DEBUG_SCOPE, GJS_DEBUG_HTTP, GJS_DEBUG_BYTE_ARRAY, GJS_DEBUG_GERROR, GJS_DEBUG_GFUNDAMENTAL, GJS_DEBUG_PROXY, } 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-3.6.1/util/misc.cpp000066400000000000000000000026551320401450000147110ustar00rootroot00000000000000/* -*- 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-3.6.1/util/misc.h000066400000000000000000000025651320401450000143560ustar00rootroot00000000000000/* -*- 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-3.6.1/verbump.py000066400000000000000000000027071320401450000143250ustar00rootroot00000000000000#!/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-3.6.1/win32/000077500000000000000000000000001320401450000132275ustar00rootroot00000000000000cjs-3.6.1/win32/Makefile.vc000066400000000000000000000032421320401450000152770ustar00rootroot00000000000000# 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-3.6.1/win32/README.txt000066400000000000000000000076361320401450000147410ustar00rootroot00000000000000Instructions 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 38 (mozjs-38) -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