jlint-3.0/0000775000175000017500000000000010066036247013240 5ustar davehodaveho00000000000000jlint-3.0/BUGS0000644000175000017500000000476007754436076013746 0ustar davehodaveho00000000000000Known bugs in Jlint ------------------- Priority (1 = highest) Bug 1 Code is not 64 bit clean (see TODO). 1 Control flow (if/while/for/exceptions) does not work (in method_desc::basic_blocks_analysis). Conditionals and jumps (in Bytecode) seem to be ignored. local_context classes are undocumented and therefore hard to use/fix. Result: many spurious warnings in cases where control flow is transferred inside a lock (e. g. with return). However, these warnings are easily verified manually (10 seconds per warning for an experienced user). 1 Empty output if single message (rather than message categories) are disabled. Current fix with grep -v... 2 shadowed variables: spurious warnings occur, functions new_field and f_forward management from Jlint 1.2 are missing in 2.X. 2 Makefile: Now uses if statements for dist target, is not portable (normal compilation/installation should still work) 2 In some circumstances, a lot of "Lock x is acquired while holding lock y, with other thread holding lock y and requesting x." warnings are given. Sometimes, the same warning is printed many times. Usually "x" and "y" were the same string (sometimes referring to unnamed local variables). These warnings are sometimes justified, sometimes certainly not. They should also only appear once. 2 "Method wait() can be invoked with monitor of other object locked." However, no list of locks given; apparently no other monitor is held. Justified warning or bug? 2 "Method xy can be called from different threads and is not synchronized." is now sometimes issued several times for same method. 2 Spurious warnings of type "Lock x is requested while holding lock , with other thread holding x and requesting lock " when a thread acquires the same lock twice. 2 "Method java/lang/Object.equals(java.lang.Object) is not overridden by method with the same name of derived class 'x'." However, argument is of type "String", not "Object" (maybe cannot be checked statically in all cases?) 2 Sometimes, (spurious) duplicate loop warnings are given, with an incorrect line number, after the real loop warning. 3 Warnings about access to "synthetic" fields or methods are given. However, these should be ignored. 3 Loop ID numbering is no longer correct. Loop numbers in warnings no longer necessarily start with "1" and are not always consecutive. This is a consequence of the extra analyses that interfere with the loop count. Loop IDs are still unique. 3 Jlint crashes if the SourceDir attribute in the .class file is broken. jlint-3.0/CHANGELOG0000644000175000017500000000323207334276566014466 0ustar davehodaveho00000000000000Jlint changelog: Version 2.3: (Trivial) port to LinuxPPC and Mac OS X. Version 2.2: Applied Eric A. Raymond's compiler compatibility patch. Removed -DHASH_TABLE in the Makefile due to some problems (it can still be enabled if needed). OpenBSD port (thanks to Kevin Lo for testing it). Version 2.1: FreeBSD Port (thanks to Ernst de Haan for giving me access to a FreeBSD box), Visual C++ port (by Maarten Breddels). Version 2.0: As a part of Cyrille Artho's master thesis, 2 1/2 weeks were spent on cleaning up and extending Jlint as follows: - Jlint has been restructured into several files. All larger classes now are in their own file, while some smaller classes have been grouped by their meaning. This makes Jlint easier to maintain. - Synchronized blocks are now fully supported. The value of references is tracked during their "life" on the stack and as static or instance variables. This allowed the following improvements: 1. wait/notify[All] calls are now truly checked: The current set of locks is analyzed. Depending on whether a thread holds the lock it is wait()ing on or whether it holds other locks, too, different (much more precise) warnings are issued. 2. Inter-method deadlocks from synchronized blocks are fully checked. The call graph analysis has been extended with "pseudo method calls" whenever a lock is being obtained. This extends the old analysis for synchronized methods. 3. Assignments to locking variables (usually hazardous) are also checked. If they are not within a constructor or guarded by a lock, a warning is issued. Version 1.11: Last version from Konstantin Knizhnik, using one source and one header file. jlint-3.0/COPYING0000644000175000017500000004306707245317103014301 0ustar davehodaveho00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. jlint-3.0/Makefile0000644000175000017500000001702610001266415014673 0ustar davehodaveho00000000000000# -*- makefile -*- # Makefile for mkmf. # Makefile for Unix and GNU/Linux with gcc/g++ compiler # Edit here: CC=gcc CPP=g++ # Hints: # if you use egcs-2.90.* version of GCC please add option -fno-exceptions # to reduce code size and increase performance # remove -DSLIST for really old systems without SGI's STL implementations ############################################################################### # Debug version #CFLAGS = -c -Wall -O0 -g -DDUMP_EDGES -DDEBUG -DDUMP_MONITOR #CFLAGS = -c -Wall -O0 -g -DDUMP_EDGES -DDUMP_BYTE_CODES -DPRINT_PC -DDUMP_STACK -DDUMP_MONITOR -DDEBUG # DSLIST DHASH_TABLE removed because it wouldn't compile otherwise on gcc 3.x # Optimized version CFLAGS = -c -Wall -O2 -g # -DSLIST removed because it wouldn't compile under gcc 3.x # add -DHASH_TABLE for extra speed (may sometimes produce inconsistent results) # Optimized version with switched off asserts #CFLAGS = -c -Wall -O2 -g -DSLIST -DNDEBUG # link zlib the compression/decompression library. used for decompressing jar files LFLAGS=-g -lz # Directory to place executables INSTALL_DIR=/usr/local/bin # Version number # VERSION=`grep VERSION jlint.hh | sed 's/.*N //'` VERSION=3.0 # Files that go into distro DISTFILES=`ls jlint-$(VERSION)/{antic.c,BUGS,Makefile,*.msg,*.hh,*.cc,*.d,README,TODO,CHANGELOG,COPYING,manual.texi,manual.html,manual.pdf,jlint.sh,mkmf.pl}` TESTDISTFILES=`ls jlint-$(VERSION)/{antic.c,BUGS,Makefile,*.msg,*.hh,*.cc,*.d,README,TODO,CHANGELOG,COPYING,manual.texi,manual.html,manual.pdf,jlint_3.0.tex,jlint.sh,mkmf.pl,runtest.sh,showdiff.sh,showerror.sh,testall.sh,README.tests,tests.tar.bz2}` # Makefile rules all: antic jlint antic.o: antic.c $(CC) $(CFLAGS) antic.c antic: antic.o $(CC) $(LFLAGS) -o antic antic.o clean: rm -f *.o *.exe core *~ *.his *.class jlint antic manual.{html,pdf,aux,cp,fn,ky,log,pg,toc,tp,vr} jlint_3.0.{aux,dvi,log,toc} if [ -d ./tests/log ]; then rm -f ./tests/log/*~ ./tests/log/*.{err,errfiles,log}; rm -f ./tests/log/test.{diff,err}; fi if [ -d ./tests ]; then rm -f ./tests/*~ tests.tar.bz2; fi doc: manual.texi texi2html -monolithic manual.texi; texi2pdf manual.texi dist: doc tarbz targz zip chmod 644 ../jlint-*.{tar.bz2,tar.gz,zip} tarbz: if [ $(VERSION) != `grep VERSION jlint.hh | sed 's/.*N //'` ]; then echo "Check version numbers!"; exit 1; fi if [ ! -e ../jlint-$(VERSION) ]; then ln -s $(PWD) ../jlint-$(VERSION); fi cd ..; tar -chv --bzip2 -f jlint-$(VERSION).tar.bz2 $(DISTFILES) targz: if [ $(VERSION) != `grep VERSION jlint.hh | sed 's/.*N //'` ]; then echo "Check version numbers!"; exit 1; fi if [ ! -e ../jlint-$(VERSION) ]; then ln -s $(PWD) ../jlint-$(VERSION); fi cd ..; tar -chvzf jlint-$(VERSION).tar.gz $(DISTFILES) zip: if [ $(VERSION) != `grep VERSION jlint.hh | sed 's/.*N //'` ]; then echo "Check version numbers!"; exit 1; fi if [ ! -e ../jlint-$(VERSION) ]; then ln -s $(PWD) ../jlint-$(VERSION); fi cd ..; rm -f jlint-$(VERSION).zip; zip -v jlint-$(VERSION).zip $(DISTFILES) `find jlint-$(VERSION)/jlintwin32 | grep -v CVS` test-dist: doc test-files-tar test-tarbz test-targz zip chmod 644 ../jlint-*.{tar.bz2,tar.gz,zip} test-files-tar: tar -chv --bzip2 -f tests.tar.bz2 tests/ test-tarbz: if [ $(VERSION) != `grep VERSION jlint.hh | sed 's/.*N //'` ]; then echo "Check version numbers!"; exit 1; fi if [ ! -e ../jlint-$(VERSION) ]; then ln -s $(PWD) ../jlint-$(VERSION); fi cd ..; tar -chv --bzip2 -f jlint-$(VERSION).tar.bz2 $(TESTDISTFILES) test-targz: if [ $(VERSION) != `grep VERSION jlint.hh | sed 's/.*N //'` ]; then echo "Check version numbers!"; exit 1; fi if [ ! -e ../jlint-$(VERSION) ]; then ln -s $(PWD) ../jlint-$(VERSION); fi cd ..; tar -chvzf jlint-$(VERSION).tar.gz $(TESTDISTFILES) test-zip: if [ $(VERSION) != `grep VERSION jlint.hh | sed 's/.*N //'` ]; then echo "Check version numbers!"; exit 1; fi if [ ! -e ../jlint-$(VERSION) ]; then ln -s $(PWD) ../jlint-$(VERSION); fi cd ..; rm -f jlint-$(VERSION).zip; zip -v jlint-$(VERSION).zip $(TESTDISTFILES) `find jlint-$(VERSION)/jlintwin32 | grep -v CVS` install: cp jlint antic jlint.sh $(INSTALL_DIR) chmod 755 $(INSTALL_DIR)/antic chmod 755 $(INSTALL_DIR)/jlint chmod 755 $(INSTALL_DIR)/jlint.sh # --> automatically generated dependencies follow; do not remove this line. jlint: \ access_desc.o \ callee_desc.o \ class_desc.o \ graph.o \ jlint.o \ local_context.o \ locks.o \ message_node.o \ method_desc.o $(CPP) $(LFLAGS) -o jlint access_desc.o callee_desc.o class_desc.o graph.o jlint.o local_context.o locks.o message_node.o method_desc.o access_desc.o: access_desc.cc \ access_desc.hh \ class_desc.hh \ functions.hh \ types.hh \ locks.hh \ utf_string.hh \ field_desc.hh \ graph.hh \ method_desc.hh \ overridden_method.hh \ jlint.d \ jlint.msg \ message_node.hh \ component_desc.hh \ inlines.hh \ var_desc.hh \ constant.hh \ callee_desc.hh \ local_context.hh \ string_pool.hh $(CPP) $(CFLAGS) access_desc.cc callee_desc.o: callee_desc.cc \ callee_desc.hh \ class_desc.hh \ functions.hh \ types.hh \ locks.hh \ utf_string.hh \ field_desc.hh \ graph.hh \ method_desc.hh \ overridden_method.hh \ jlint.d \ jlint.msg \ message_node.hh \ component_desc.hh \ inlines.hh \ var_desc.hh \ constant.hh \ local_context.hh \ access_desc.hh \ string_pool.hh $(CPP) $(CFLAGS) callee_desc.cc class_desc.o: class_desc.cc \ class_desc.hh \ types.hh \ locks.hh \ utf_string.hh \ field_desc.hh \ graph.hh \ method_desc.hh \ overridden_method.hh \ jlint.d \ jlint.msg \ functions.hh \ message_node.hh \ component_desc.hh \ inlines.hh \ var_desc.hh \ constant.hh \ callee_desc.hh \ local_context.hh \ access_desc.hh \ string_pool.hh $(CPP) $(CFLAGS) class_desc.cc graph.o: graph.cc \ graph.hh \ types.hh \ method_desc.hh \ jlint.d \ jlint.msg \ inlines.hh \ component_desc.hh \ var_desc.hh \ constant.hh \ class_desc.hh \ callee_desc.hh \ field_desc.hh \ local_context.hh \ functions.hh \ access_desc.hh \ string_pool.hh \ locks.hh \ utf_string.hh \ message_node.hh \ overridden_method.hh $(CPP) $(CFLAGS) graph.cc jlint.o: jlint.cc \ jlint.hh \ jlint.msg \ types.hh \ message_node.hh \ utf_string.hh \ method_desc.hh \ field_desc.hh \ class_desc.hh \ constant.hh \ callee_desc.hh \ access_desc.hh \ graph.hh \ component_desc.hh \ var_desc.hh \ local_context.hh \ overridden_method.hh \ string_pool.hh \ jlint.d \ functions.hh \ inlines.hh \ locks.hh $(CPP) $(CFLAGS) jlint.cc local_context.o: local_context.cc \ local_context.hh \ types.hh \ var_desc.hh \ method_desc.hh \ jlint.d \ jlint.msg \ utf_string.hh \ functions.hh \ message_node.hh \ inlines.hh \ component_desc.hh \ constant.hh \ class_desc.hh \ callee_desc.hh \ field_desc.hh \ access_desc.hh \ string_pool.hh \ locks.hh \ graph.hh \ overridden_method.hh $(CPP) $(CFLAGS) local_context.cc locks.o: locks.cc \ locks.hh \ types.hh \ field_desc.hh \ jlint.d \ jlint.msg \ component_desc.hh \ utf_string.hh \ functions.hh \ message_node.hh $(CPP) $(CFLAGS) locks.cc message_node.o: message_node.cc \ message_node.hh \ functions.hh \ types.hh \ jlint.d \ jlint.msg $(CPP) $(CFLAGS) message_node.cc method_desc.o: method_desc.cc \ method_desc.hh \ types.hh \ inlines.hh \ component_desc.hh \ var_desc.hh \ constant.hh \ class_desc.hh \ callee_desc.hh \ field_desc.hh \ local_context.hh \ functions.hh \ access_desc.hh \ string_pool.hh \ locks.hh \ jlint.d \ jlint.msg \ utf_string.hh \ message_node.hh \ graph.hh \ overridden_method.hh $(CPP) $(CFLAGS) method_desc.cc # --> end of automatically generated dependencies; do not remove this line. jlint-3.0/README0000644000175000017500000000447707734360365014143 0ustar davehodaveho00000000000000Jlint 3.0 README ---------------- Description: ------------ Jlint will check your Java code and find bugs, inconsistencies and synchronization problems by doing data flow analysis and building a lock graph. Original version (1.11) by Konstantin Knizhnik. Enhanced version (2.0) and a few bugfixes by Cyrille Artho. Versions 2.1 and newer: FreeBSD port by Cyrille Artho; Visual C++ port by Maarten Breddels; compatibility patch for older compilers by Eric A. Raymond; OpenBSD patch by Kevin Lo. LinuxPPC and MacOS X port (both trivial) by Cyrille Artho. Version 3.0: works with java 1.4, added test framework by Raphael Ackermann; Support for compressed zip/jar files by Mark Wutka; Patch for class_desc.cc by Jeroen Mostert Usage: ------ User guide is found in manual.html. Installation: ------------ - System requirements: For compiling: C++ compiler, C++ Standard Library with Standard Templates. For compiling the docs: Texinfo; type "make doc". For using: Any Java compiler (no Java Run Time Environment needed). Recommended: A Unix shell (e. g. bash) to run jlint.sh. - Installation: 1. Unpack the archive with unzip jlint.zip (MS-DOS) or gunzip -c jlint.tar.gz | tar -xvf - 2. cd jlint 3. make ; make install Compilation problems: --------------------- If Jlint won't compile for your system, remove the flag -DSLIST in the Makefile, or upgrade to a newer version of the STL. - Windows users: To have .cc files be recognized as C++ source code, check out http://support.microsoft.com/support/kb/articles/Q181/5/06.ASP - MacOS X users: In the first few lines of the Makefile (lines 6 and 7), replace gcc with cc, and g++ with c++. Alternatively, you can create a symbolic link for the compiler executables. - Creating a new Makefile: You can update the Makefile by typing ./mkmf.pl jlint antic readme __tmp__ ; mv __tmp__ Makefile - Supported platforms: Jlint is known to work with recent versions of Linux, Solaris, FreeBSD, OpenBSD, Windows. There is no user support available, though, as this program is not commercial. Bugs: ----- Send bug reports to cyrille (at) artho.com, Subject: Jlint. Acknowledgements: ----------------- Thanks to all the people mentioned above. Thanks to Eric A. Raymond for his help with compiler compatibility problems. Special thanks to Ernst de Haan for providing a FreeBSD account for testing. jlint-3.0/TODO0000644000175000017500000000536007700512252013726 0ustar davehodaveho00000000000000To do ===== * (1) Make code 64 bit clean by adding more type casts. E.g. void *p; int i; p = (void *) (ptrword) i; where ptrword is derived from a ./configure script. * (1) Restructuring/rewriting of the data structures: Some classes are poorly designed, and in general the given framework is inadequate for a more powerful analysis (such as the analysis of lock sets when calls from one class to another class are made). * (2) Context merging (return, exceptions, if, while, for) to eliminate some spurious warnings. See section [full-control-flow-analysis]. * (2) Suppressing unnecessary warnings for variables and methods that are read-only. A resource that is shared for reading is a very common scenario in real software. Therefore, Jlint should suppress a warning about concurrent variable or method accesses if the following conditions hold: Shared-read variables: The variable is not accessed outside the constructor or synchronized(this) blocks. Shared-read methods: The method does not access fields that are not shared for reading (according to the definition above). * (2) Adding a check for "const methods" (as in C++): In C++, a method which is declared const may not change the this instance or any const (final) fields. As a consequence of that, calls to non-const methods and the usage of const fields as non-const arguments (in method calls) are forbidden. This is one of the few areas where C++ is more type safe than Java. Adding such a check to Jlint (const annotations could be stored in a template) should not be very hard, and would be a minor extension of "shared-read methods" (see above). * (2) Full analysis of lock sets across method/class boundaries for wait and notify calls. * (2) Checking the visibility of object instances in other threads: A non-synchronized method where the callee instance is not visible outside the current thread (e.g. as a local variable) can be called without any danger of race conditions. In such cases, a warning should be omitted. Verifying this requires evaluating accessor information. * (2) Fixing the fault in the warning selection switches: right now, switching warnings on and off based on their message code produces an empty output. * (2) Improving verbosity/information content of some (existing) warnings. * (2) Adding an improved selectability for warnings, e.g. by a severity level or likelihood. * (3) Support for locks on classes (getClass) in synchronizations. * (3) Eliminating spurious warnings where implicit synchronization exists (e.g. streams). * (3) Texinfo documentation: fixing old spelling and grammar mistakes from version 1.11, better Texinfo tagging of text (e.g. @var), full @node tree for document. jlint-3.0/access_desc.cc0000644000175000017500000000027607231662275016017 0ustar davehodaveho00000000000000#include "access_desc.hh" void access_desc::message(int code, ...) { va_list ap; va_start(ap, code); format_message(code, self_class->source_file, line, ap); va_end(ap); } jlint-3.0/access_desc.hh0000644000175000017500000000126407236032035016015 0ustar davehodaveho00000000000000#ifndef ACCESS_DESC_HH #define ACCESS_DESC_HH #include class field_desc; #include "class_desc.hh" #include "functions.hh" class access_desc { public: access_desc* next; class_desc* self_class; field_desc* field; int line; int attr; enum { a_self = 0x01, // access to the component of the same class a_new = 0x02 // field of newly created object is accessed }; void message(int msg_code, ...); access_desc(field_desc* desc, class_desc* cls, int lineno, access_desc* chain) { field = desc; next = chain; self_class = cls; line = lineno; attr = 0; } }; #endif jlint-3.0/antic.c0000644000175000017500000007063107231104464014504 0ustar davehodaveho00000000000000/*-< ANTIC.C >------------------------------------------------------*--------*/ /* Jlint Version 1.11.1 (c) 1998 GARRET * ? */ /* (Java Lint) * /\| */ /* * / \ */ /* Created: 28-Apr-1998 K.A. Knizhnik * / [] \ */ /* Last update: 24-Feb-2000 E.G. Parmelan * GARRET */ /*------------------------------------------------------------------*--------*/ /* AntiC - C/C++/Java syntax verifier * */ /*------------------------------------------------------------------*--------*/ /* Edouard G. Parmelan e-mail address: egp@free.fr */ /*------------------------------------------------------------------*--------*/ #include #include #include #ifdef _WIN32 #include #else #include #endif #ifndef MAX_PATH #define MAX_PATH 1024 #endif #define VERSION "1.11.1" char* file_name; FILE* f; int line; int coln; int force_java; int token_line; int token_coln; int java; int fall_thru_cmt; int notreached_cmt; int tab_size = 8; int relax_else = 0; /* Use only lowercase letters without spaces */ const char *fall_thru_cmt_text[] = { "fallthr", "nobreak" }; /* Use only lowercase letters without spaces */ const char *notreached_cmt_text[] = { "notreach", "unreach", }; #define items(a) (sizeof(a)/sizeof(*(a))) #define nobreak enum { TKN_FOR = 257, TKN_IF, TKN_DO, TKN_WHILE, TKN_ELSE, TKN_BREAK, TKN_CASE, TKN_CATCH, TKN_FINALLY, TKN_TRY, TKN_SWITCH, TKN_STR, TKN_IDENT, TKN_CTX, TKN_ACCESS, TKN_AND_OP, TKN_OR_OP, TKN_BIN_OP, TKN_BIT_OP, TKN_SET_OP, TKN_CMP_OP, TKN_EQU_OP, TKN_INC_OP, TKN_SHIFT_OP, TKN_INCLUDE }; typedef enum { ctx_statement, ctx_typedef, ctx_body, ctx_switch } statement_ctx; int get() { int ch = getc(f); if (ch == '\n') { line += 1; coln = 0; } else if (ch == '\t') { coln += tab_size; coln -= coln % tab_size; } else { coln += 1; } return ch; } void unget(int ch) { ungetc(ch, f); if (ch == '\n') { line -= 1; } else if (ch == '\t') { coln -= tab_size; } else { coln -= 1; } } int n_messages; void message_at(int line, int coln, char* msg) { printf("%s:%d:%d: %s\n", file_name, line, coln, msg); n_messages += 1; } void message(char* msg) { message_at(token_line, token_coln, msg); } int check_special_character() { int ch = get(); switch (ch) { case '\n': if (java) { message("Newline within string\n"); } break; case 'n': case 't': case 'b': case 'r': case 'f': case '\\': case '"': case '\'': break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': ch = get(); if (ch >= '0' && ch <= '9') { if (ch > '7') { message("Octal digit expected"); } ch = get(); if (ch >= '0' && ch <= '9') { if (ch > '7') { message("Octal digit expected"); } ch = get(); if (ch >= '0' && ch <= '9') { message("May be more than three octal digits are specified"); } } } return ch; case 'x': if (java) { message("Hexadecimal constant\n"); } ch = get(); if (!isxdigit(ch)) { message("Hexadecimal digit expected\n"); } else while (isxdigit(ch)) { ch = get(); } return ch; case 'u': if (java) { do { ch = get(); } while (ch == 'u'); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (isxdigit(ch)) { message("May be more than four hex digits " "are specified for character constant"); } return ch; } nobreak; default: message("May be incorrect escape sequence"); } return get(); } static void check_fall_thru(int ch) { static unsigned index[items(fall_thru_cmt_text)]; unsigned i; if (ch == ' ' || ch == '-') { return; } if (ch == 0) { fall_thru_cmt = 0; for (i = 0; i < items(fall_thru_cmt_text); i++) index[i] = 0; } else if (!fall_thru_cmt) { for (i = 0; i < items(index); i++) { if (tolower(ch) == fall_thru_cmt_text[i][index[i]]) { if (fall_thru_cmt_text[i][++(index[i])] == '\0') { fall_thru_cmt = 1; return; } } else { index[i] = 0; } } } } static void check_notreached(int ch) { static unsigned index[items(notreached_cmt_text)]; unsigned i; if (ch == ' ' || ch == '-') { return; } if (ch == 0) { notreached_cmt = 0; for (i = 0; i < items(notreached_cmt_text); i++) index[i] = 0; } else if (!notreached_cmt) { for (i = 0; i < items(index); i++) { if (tolower(ch) == notreached_cmt_text[i][index[i]]) { if (notreached_cmt_text[i][++(index[i])] == '\0') { notreached_cmt = 1; return; } } else { index[i] = 0; } } } } int scan_0(int cpp) { int ch; unsigned i; char buf[256]; skip_spaces: do { ch = get(); if ((ch == EOF) || (ch == '\n') || (ch == '#')) return ch; } while (isspace(ch)); token_coln = coln; token_line = line; switch (ch) { case '/': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '*') { int nested = 0; ch = get(); while (1) { if (ch == EOF) { message("Unclosed comments"); return EOF; } else if (ch == '/') { ch = get(); if (ch == '*') { if (!nested) message("Nested comments"); nested = 1; } } else if (ch == '*') { ch = get(); if (ch == '/') { goto skip_spaces; } } else { check_fall_thru(ch); check_notreached(ch); ch = get(); } } } else if (ch == '/') { while ((ch = get()) != '\n' && ch != EOF) { check_fall_thru(ch); check_notreached(ch); } return ch; } unget(ch); return TKN_BIN_OP; case '"': ch = get(); while (ch != '"') { switch (ch) { case '\n': message("Newline within string constant"); ch = get(); continue; case EOF: return EOF; case '\\': if (cpp == 2) { ch = get(); } else { ch = check_special_character(); } continue; case '?': if (!java) { ch = get(); if (ch == '?') { ch = get(); if (ch == '=' || ch == '/' || ch == '\'' || ch == '(' || ch == ')' || ch == '!' || ch == '<' || ch == '>') { message("Trigraph sequence inside string"); } } continue; } nobreak; default: ch = get(); } } return TKN_STR; case '\'': ch = get(); if (ch == '\\') { ch = check_special_character(); } else { ch = get(); } if (ch != '\'') { message("Multibyte character constants are not portable"); do { ch = get(); if (ch == '\\') get(); } while ((ch != '\'') && (ch != EOF)); } return TKN_STR; case ':': if (!java) { ch = get(); if (ch == ':') { return TKN_CTX; } else { unget(ch); return ':'; } } break; case '+': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '+') return TKN_INC_OP; unget(ch); return '+'; case '-': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (!java && ch == '>') { return TKN_ACCESS; } else if (ch == '-') return TKN_INC_OP; unget(ch); return '-'; case '*': ch = get(); if (ch == '=') { return TKN_SET_OP; } unget(ch); return '*'; case '%': ch = get(); if (ch == '=') { return TKN_SET_OP; } unget(ch); return TKN_BIN_OP; case '&': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '&') { return TKN_AND_OP; } unget(ch); return '&'; case '|': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '|') { return TKN_OR_OP; } unget(ch); return TKN_BIT_OP; case '^': ch = get(); if (ch == '=') { return TKN_SET_OP; } unget(ch); return TKN_BIT_OP; case '>': ch = get(); if (ch == '>') { ch = get(); if (java && ch == '>') { ch = get(); if (ch == '=') return TKN_SET_OP; } else if (ch == '=') return TKN_SET_OP; unget(ch); return TKN_SHIFT_OP; } else if (ch != '=') unget(ch); return TKN_CMP_OP; case '<': ch = get(); if (ch == '<') { ch = get(); if (ch == '=') return TKN_SET_OP; unget(ch); return TKN_SHIFT_OP; } else if (ch != '=') unget(ch); return TKN_CMP_OP; case '=': ch = get(); if (ch == '=') return TKN_EQU_OP; unget(ch); return '='; case '!': ch = get(); if (ch == '=') return TKN_CMP_OP; unget(ch); return '!'; default: if (isalnum(ch) || ch == '_' || ch == '$') { i = 0; do { if (i < sizeof(buf)) { buf[i++] = ch; } ch = get(); } while (isalnum(ch) || ch == '_' || ch == '$'); unget(ch); if (i < sizeof(buf)) { buf[i] = '\0'; if (strcmp(buf, "if") == 0) return TKN_IF; if (strcmp(buf, "for") == 0) return TKN_FOR; if (strcmp(buf, "while") == 0) return TKN_WHILE; if (strcmp(buf, "do") == 0) return TKN_DO; if (strcmp(buf, "else") == 0) return TKN_ELSE; if (strcmp(buf, "default") == 0) return TKN_CASE; if (strcmp(buf, "case") == 0) return TKN_CASE; if (strcmp(buf, "break") == 0) return TKN_BREAK; if (strcmp(buf, "goto") == 0) return TKN_BREAK; if (strcmp(buf, "throw") == 0) return TKN_BREAK; if (strcmp(buf, "return") == 0) return TKN_BREAK; if (strcmp(buf, "continue") == 0) return TKN_BREAK; if (strcmp(buf, "catch") == 0) return TKN_CATCH; if (strcmp(buf, "try") == 0) return TKN_TRY; if (strcmp(buf, "finally") == 0) return TKN_FINALLY; if (strcmp(buf, "switch") == 0) return TKN_SWITCH; if (!java && strcmp(buf, "nobreak") == 0) return TKN_BREAK; if ((cpp == 1) && strcmp(buf, "include") == 0) return TKN_INCLUDE; } if (isdigit((unsigned char)buf[0]) && buf[i-1] == 'l') { message("May be 'l' is used instead of '1' at the end of " "integer constant"); } return TKN_IDENT; } } return ch; } int scan() { int ch; check_fall_thru(0); check_notreached(0); while (1) { ch = scan_0(0); if (ch == '#') { int cpp = 1; do { ch = scan_0(cpp); if (cpp == 1 && ch == TKN_INCLUDE) cpp = 2; if (ch == '\\') scan_0(cpp); } while ((ch != '\n') && (ch != EOF)); } else if (ch == '\\') { message("Unexpected character '\\'"); } else if (ch != '\n') return ch; } } int parse_binary_operation(int* n_bin_ops); int parse_expression() { int lex, prev_op = 0; while (1) { int n_bin_ops; lex = parse_binary_operation(&n_bin_ops); if (lex == '&') lex = TKN_BIT_OP; if (lex == TKN_CMP_OP || lex == TKN_BIT_OP || lex == TKN_AND_OP || lex == TKN_OR_OP || lex == TKN_SET_OP || lex == '=' || lex == TKN_EQU_OP || lex == ',' || lex == '?' || lex == ':') { if (!java && lex == TKN_CMP_OP && prev_op == TKN_CMP_OP) { prev_op = 0; /* may be template */ continue; } if (((lex == TKN_CMP_OP || lex == TKN_EQU_OP) && prev_op == TKN_BIT_OP) || (lex == TKN_BIT_OP && (n_bin_ops != 0 || prev_op == TKN_AND_OP || prev_op == TKN_OR_OP || prev_op == TKN_CMP_OP || prev_op == TKN_EQU_OP)) || ((lex == TKN_AND_OP || lex == TKN_OR_OP) && (prev_op == TKN_BIT_OP || prev_op == '=' || prev_op == TKN_SET_OP)) || ((lex == '=' || lex == TKN_SET_OP) && (prev_op == TKN_AND_OP || prev_op == TKN_OR_OP))) { message("May be wrong assumption about operators priorities"); } else if (lex == TKN_AND_OP && prev_op == TKN_OR_OP) { message("May be wrong assumption about logical " "operators precedence"); } prev_op = lex; } else return lex; } } void parse_block(int* pass_through, statement_ctx ctx); int parse_term() { int lex; while ((lex = scan()) == '&' || lex == '+' || lex == '-' || lex == '*' || lex == '!' || lex == TKN_INC_OP || lex == TKN_CTX || lex == '~'); while (lex == '.' || lex == TKN_INC_OP || lex == TKN_IDENT || lex == TKN_ACCESS || lex == '(' || lex == '[' || lex == TKN_STR || lex == TKN_CTX) { if (lex == '(') { int lpr_line = token_line; int lpr_coln = token_coln; lex = parse_expression(); if (lex != ')') { message_at(lpr_line, lpr_coln, "Unbalanced '('"); return lex; } } else if (lex == '[') { int lbr_line = token_line; int lbr_coln = token_coln; lex = parse_expression(); if (lex != ']') { message_at(lbr_line, lbr_coln, "Unbalanced '['"); return lex; } } lex = scan(); } return lex; } int parse_switch_expression() { int lex = scan(); int lpr_line = token_line; int lpr_coln = token_coln; if (lex != '(') { message_at(lpr_line, lpr_coln, "'(' expected"); return lex; } lex = parse_expression(); if (lex != ')') { message_at(lpr_line, lpr_coln, "Unbalanced '('"); } return scan(); } int parse_binary_operation(int* n_bin_ops) { int lex; int prev_op = 0; *n_bin_ops = 0; while (1) { lex = parse_term(); if (lex == '*' || lex == '-' || lex == '+') lex = TKN_BIN_OP; if (lex == TKN_SHIFT_OP) { if (prev_op == TKN_BIN_OP) { message("May be wrong assumption about shift operator priority"); } } else if (lex == TKN_BIN_OP) { if (prev_op == TKN_SHIFT_OP) { message("May be wrong assumption about shift operator priority"); } } else if (java && lex == '{') { /* anonymous class */ int pass_through = 1; parse_block(&pass_through, ctx_typedef); } else { return lex; } prev_op = lex; *n_bin_ops += 1; } } void parse_conditional_expression(int term_ch) { int bit_op_line = 0; int bit_op_coln = 0; int n_bin_ops; int lex; int expr_line = 0; int expr_coln = 0; int prev_op = 0; if (term_ch == ')') { if (scan() != '(') { message("'(' expected"); return; } expr_line = token_line; expr_coln = token_coln; } while ((lex = parse_binary_operation(&n_bin_ops)) != term_ch) { if (lex == '&') lex = TKN_BIT_OP; if (lex == EOF) { message_at(expr_line, expr_coln, term_ch == ')' ? "Unbalanced parentheses" : "No ')' after FOR condition"); return; } if (lex == '=') { message("May be '=' used instead of '=='"); } else if (lex == TKN_SET_OP) { message("May be skipped parentheses around assign operator"); } else if (lex == TKN_BIT_OP) { if (n_bin_ops != 0 || prev_op == TKN_AND_OP || prev_op == TKN_OR_OP || prev_op == TKN_EQU_OP || prev_op == TKN_CMP_OP) { message("May be wrong assumption about bit operation priority"); } bit_op_line = token_line; bit_op_coln = token_coln; prev_op = TKN_BIT_OP; continue; } else if (lex == TKN_AND_OP && prev_op == TKN_OR_OP) { message("May be wrong assumption about logical operators precedence"); } else if (lex != '?' && lex != ':' && lex != ',' && lex != TKN_CMP_OP && lex != TKN_EQU_OP && lex != TKN_AND_OP && lex != TKN_OR_OP) { message("Syntax error"); } if (prev_op == TKN_BIT_OP) { message_at(bit_op_line, bit_op_coln, "May be wrong assumption about operators priorities"); } prev_op = lex; } } int parse_statement(int lex, int* stmt_line, int* stmt_coln, int* pass_through, int* entry_point, statement_ctx ctx) { int next_stmt_line, next_stmt_coln; int if_coln; int stmt; int if_pass_through, else_pass_through; int statement_with_label = 0; int n_labels_before; int entries; static int n_labels; int miss_equal; classify_statement: miss_equal = 0; switch (lex) { case '{': parse_block(pass_through, ctx); nobreak; case ';': lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; case TKN_FOR: if ((lex = scan()) != '(') { message("No '(' after FOR"); return lex; } lex = parse_expression(); if (lex != ';') { message("No ';' after FOR initialization part"); return lex; } parse_conditional_expression(';'); lex = parse_expression(); if (lex != ')') { message("No ')' after FOR increment part"); return lex; } goto loop_body; case TKN_WHILE: parse_conditional_expression(')'); loop_body: stmt = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; if_pass_through = 0; n_labels_before = n_labels; if (stmt != '{' && next_stmt_coln <= *stmt_coln) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about loop body (semicolon missed)"); } lex = parse_statement(stmt, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); if (if_pass_through) { *pass_through = 1; } if (!java && n_labels != n_labels_before) { *entry_point = 1; } if (stmt != '{' && lex != '}' && next_stmt_coln > *stmt_coln) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about loop body"); } break; case TKN_SWITCH: stmt = parse_switch_expression(); next_stmt_line = token_line; next_stmt_coln = token_coln; if (stmt != '{') { message_at(next_stmt_line, next_stmt_coln, "Suspicios SWITCH without body"); } if_pass_through = 0; lex = parse_statement(stmt, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_switch); *pass_through = 1; break; case TKN_IF: if_coln = *stmt_coln; parse_conditional_expression(')'); stmt = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; else_pass_through = if_pass_through = *pass_through; lex = parse_statement(stmt, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); if (if_pass_through) { *pass_through = 1; } if (lex == TKN_ELSE) { lex = scan(); if (next_stmt_coln < if_coln) { /* handle IF ... ELSE FOO where FOO is aligned with IF */ if (!relax_else || (token_coln < if_coln)) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about ELSE branch " "association"); } } next_stmt_coln = token_coln; if (lex == TKN_IF && next_stmt_line == token_line) { if (if_coln < token_coln) { next_stmt_coln = if_coln; } } next_stmt_line = token_line; lex = parse_statement(lex, &next_stmt_line, &next_stmt_coln, &else_pass_through, &entries, ctx_statement); if (!if_pass_through && !else_pass_through) { *pass_through = 0; } else if (else_pass_through) { *pass_through = 1; } } else if (stmt == ';' || (stmt != '{' && lex != '}' && next_stmt_coln > if_coln)) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about IF body"); } break; case TKN_TRY: lex = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; if_pass_through = *pass_through; else_pass_through = 0; lex = parse_statement(lex, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); else_pass_through |= if_pass_through; while ((lex == TKN_CATCH) || (lex == TKN_FINALLY)) { /* It is really not condition expression, but list of * catch paramters, but is possible to use this function to skip * catch paramter list. */ if (lex == TKN_CATCH) parse_conditional_expression(')'); if_pass_through = *pass_through; lex = parse_statement(scan(), &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); else_pass_through |= if_pass_through; } *pass_through = else_pass_through; break; case TKN_DO: lex = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; n_labels_before = n_labels; if_pass_through = 0; lex = parse_statement(lex, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); if (if_pass_through) { *pass_through = 1; } if (!java && n_labels != n_labels_before) { *entry_point = 1; } if (lex != TKN_WHILE) { message_at(next_stmt_line, next_stmt_coln, "No WHILE for DO"); } parse_conditional_expression(')'); lex = scan(); if (lex != ';') { message_at(token_line, token_coln, "No ';' after DO WHILE"); } lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; case TKN_CASE: if (ctx != ctx_switch) { message_at(*stmt_line, *stmt_coln, "Suspicious CASE/DEFAULT"); } else if (!statement_with_label && !fall_thru_cmt && *pass_through) { message_at(*stmt_line, *stmt_coln, "Possible miss of BREAK before CASE/DEFAULT"); } while ((lex = scan()) != ':') { if (lex == EOF) { message_at(*stmt_line, *stmt_coln, "No ':' after CASE"); return EOF; } } statement_with_label = 1; *entry_point = 1; *pass_through = 1; lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; case TKN_BREAK: *pass_through = 0; goto parse_statement; case TKN_CATCH: case TKN_FINALLY: message_at(*stmt_line, *stmt_coln, "Suspicious CATCH/FINALLY"); if (lex == TKN_CATCH) parse_conditional_expression(')'); *pass_through = 1; lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; case TKN_IDENT: lex = scan(); if (lex == ':') { n_labels += 1; statement_with_label = 1; *entry_point = 1; *pass_through = 1; lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; } miss_equal = 1; nobreak; parse_statement: default: /* ctx = ctx_typedef; */ while (lex != ';') { switch (lex) { case EOF: message_at(*stmt_line, *stmt_coln, "Statement not terminated by ';'"); return EOF; case '(': lex = parse_expression(); if (lex != ')') { message("')' expected"); return lex; } ctx = ctx_body; miss_equal = 0; break; case '[': lex = parse_expression(); if (lex != ']') { message("']' expected"); return lex; } break; case ')': message("Unbalanced ')' in statement"); lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; case TKN_EQU_OP: if (miss_equal && ctx != ctx_typedef) message("Possible miss of '='"); miss_equal = 0; break; case TKN_SET_OP: case '=': lex = parse_expression(); miss_equal = 0; continue; case '}': *stmt_line = token_line; *stmt_coln = token_coln; return lex; case '{': if_pass_through = 1; parse_block(&if_pass_through, ctx); lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; case TKN_FOR: case TKN_IF: case TKN_DO: case TKN_WHILE: case TKN_CASE: case TKN_TRY: case TKN_SWITCH: message_at(*stmt_line, *stmt_coln, "Possible miss of ';'"); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; } lex = scan(); } lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; } *stmt_line = next_stmt_line; *stmt_coln = next_stmt_coln; return lex; } void parse_block(int* pass_through, statement_ctx ctx) { int block_line = token_line; int block_coln = token_coln; int lex = scan(); int stmt_line = token_line; int stmt_coln = token_coln; int reachable = 1; int unreachable = 0; int unreachable_line; int unreachable_coln; int entry_point; while (1) { switch (lex) { case EOF: message_at(block_line, block_coln, "Unbalanced brackets"); return; case '}': if (notreached_cmt) { *pass_through = 1; } return; case TKN_ELSE: message_at(stmt_line, stmt_coln, "ELSE without IF"); lex = scan(); continue; case TKN_FOR: case TKN_IF: case TKN_DO: case TKN_WHILE: case TKN_BREAK: case TKN_CASE: case TKN_CATCH: case TKN_FINALLY: case TKN_TRY: case TKN_SWITCH: if (ctx == ctx_typedef) ctx = ctx_statement; nobreak; default: if (lex == TKN_CASE) reachable = 1; else reachable = *pass_through; unreachable_line = stmt_line; unreachable_coln = stmt_coln; entry_point = 0; lex = parse_statement(lex, &stmt_line, &stmt_coln, pass_through, &entry_point, ctx); if (ctx != ctx_typedef && unreachable && !entry_point) { message_at(unreachable_line, unreachable_coln, "Unreachable statement"); } if (reachable && !*pass_through && !notreached_cmt) { unreachable = 1; } else { unreachable = 0; } } } } void parse_file() { int lex = scan(); while (lex != EOF) { if (lex == '{') { int pass_through = 1; parse_block(&pass_through, ctx_typedef); } else if (lex == '=') { lex = parse_expression(); continue; } lex = scan(); } } int has_suffix(char* str, char* suffix) { int suffix_len = strlen(suffix); int str_len = strlen(str); if (suffix_len > str_len) { return 0; } str += str_len - suffix_len; while (--suffix_len >= 0) { if (tolower(*(unsigned char*)str) != *suffix) { return 0; } suffix += 1; str += 1; } return 1; } void load_file(char* name, int recursive) { #ifdef _WIN32 HANDLE dir; char dir_path[MAX_PATH]; WIN32_FIND_DATA file_data; if (recursive != 0) { sprintf(dir_path, "%s\\*", name); if ((dir=FindFirstFile(dir_path, &file_data)) != INVALID_HANDLE_VALUE) { name = dir_path; } } else { if (strcmp(name, "..") == 0 || strcmp(name, ".") == 0) { load_file(name, 1); return; } if ((dir = FindFirstFile(name, &file_data)) == INVALID_HANDLE_VALUE) { fprintf(stderr, "Failed to locate file '%s'\n", name); return; } } if (dir != INVALID_HANDLE_VALUE) { do { if (!recursive || *file_data.cFileName != '.') { char file_path[MAX_PATH]; char* file_dir = strrchr(name, '\\'); char* file_name_with_path; if (file_dir != NULL) { int dir_len = file_dir - name + 1; memcpy(file_path, name, dir_len); strcpy(file_path+dir_len, file_data.cFileName); file_name_with_path = file_path; } else { file_name_with_path = file_data.cFileName; } load_file(file_name_with_path, recursive+1); } } while (FindNextFile(dir, &file_data)); CloseHandle(dir); return; } #else DIR* dir = opendir(name); if (dir != NULL) { struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (*entry->d_name != '.') { char full_name[MAX_PATH]; sprintf(full_name, "%s/%s", name, entry->d_name); load_file(full_name, 2); } } closedir(dir); return; } #endif if (recursive < 2 || has_suffix(name, ".java") || has_suffix(name, ".c") || has_suffix(name, ".cpp") || has_suffix(name, ".cxx") || has_suffix(name, ".cc") || has_suffix(name, ".h") || has_suffix(name, ".hpp")) { if ((f = fopen(name, "r")) != NULL) { file_name = name; line = 1; coln = 0; java = force_java || has_suffix(name, ".java"); parse_file(); fclose(f); } else { fprintf(stderr, "Failed to open file '%s'\n", name); } } } void usage() { fprintf(stderr, "Usage: antic [-version] [-java] [-tab ] [-relax-else] file|dir {file|dir...}\n"); } int main(int argc, char* argv[]) { int i; if (argc == 1) { usage(); return 0; } for (i = 1; i < argc; i++) { if (*argv[i] == '-') { if (strcmp(argv[i], "-java") == 0) { force_java = 1; } else if (strcmp(argv[i], "-tab") == 0 && i+1 < argc) { if (sscanf(argv[++i], "%d", &tab_size) != 1) { tab_size = 8; fprintf(stderr, "Bad value '%s' for TAB size parameter\n", argv[i]); } } else if (strcmp(argv[i], "-relax-else") == 0) { relax_else = 1; } else if (strcmp(argv[i], "-version") == 0) { fprintf(stderr, "AntiC - C/C++/Java syntax verifier" ", version %s (" __DATE__ ")\n", VERSION); return 0; } else { fprintf(stderr, "Unrecognized option %s\n", argv[i]); usage(); } } else { load_file(argv[i], 0); } } printf("Verification completed: %d reported messages\n", n_messages); return 0; } jlint-3.0/callee_desc.cc0000644000175000017500000000027607231662275016003 0ustar davehodaveho00000000000000#include "callee_desc.hh" void callee_desc::message(int code, ...) { va_list ap; va_start(ap, code); format_message(code, self_class->source_file, line, ap); va_end(ap); } jlint-3.0/callee_desc.hh0000644000175000017500000000161007241031200015761 0ustar davehodaveho00000000000000#ifndef CALLEE_DESC_HH #define CALLEE_DESC_HH #include #include class method_desc; class field_desc; class constant; #include "class_desc.hh" #include "functions.hh" class callee_desc { public: class_desc* self_class; method_desc* method; callee_desc* next; void* backtrace; int line; int attr; enum { i_self = 0x01, // invoke method of self class i_synchronized = 0x02, // method is invoked from synchronized(){} body i_wait_deadlock = 0x04 // invocation can cause deadlock in wait() }; void message(int msg_code, ...); callee_desc(class_desc* cls, method_desc* mth, callee_desc* chain, int lineno, int call_attr) { self_class = cls; method = mth; next = chain; line = lineno; attr = call_attr; backtrace = NULL; } }; #endif jlint-3.0/class_desc.cc0000644000175000017500000001520707726132443015661 0ustar davehodaveho00000000000000#include "class_desc.hh" class_desc::class_desc(utf_string const& str) : name(str), source_file(str+".java", true) { fields = NULL; methods = NULL; n_bases = 0; attr = cl_system; class_vertex = new graph_vertex(this); metaclass_vertex = new graph_vertex(this); next = chain; chain = this; } class_desc* class_desc::get(utf_string const& str) { unsigned h = str.hash() % class_hash_table_size; class_desc* cls; for (cls = hash_table[h]; cls != NULL; cls = cls->collision_chain) { if (str == cls->name) { return cls; } } cls = new class_desc(str); cls->collision_chain = hash_table[h]; hash_table[h] = cls; n_classes += 1; return cls; } method_desc* class_desc::get_method(utf_string const& mth_name, utf_string const& mth_desc) { for (method_desc* method = methods; method != NULL; method = method->next){ if (method->name == mth_name && method->desc == mth_desc) { return method; } } return methods = new method_desc(mth_name, mth_desc, this, methods); } field_desc* class_desc::get_field(utf_string const& field_name) { for (field_desc* field = fields; field != NULL; field = field->next) { if (field->name == field_name) return field; } return fields = new field_desc(field_name, this, fields); } bool class_desc::isa(const char* cls_name) { for (class_desc* cls = this; cls->n_bases != 0; cls = *cls->bases) { if (cls->name == cls_name) return true; } return false; } bool class_desc::isa(class_desc* base_class) { for (class_desc* cls = this; cls->n_bases != 0; cls = *cls->bases) { if (cls == base_class) return true; } return false; } bool class_desc::implements(const char* interface_name) { if (name == interface_name) { return true; } for (int i = n_bases; --i >= 0;) { if (bases[i]->implements(interface_name)) { return true; } } return false; } bool class_desc::in_relationship_with(class_desc* cls) { return isa(cls) || cls->isa(this); } void class_desc::check_inheritance(class_desc* derived) { for (field_desc* bf = fields; bf != NULL; bf = bf->next) { for (field_desc* df = derived->fields; df != NULL; df = df->next) { if (bf->name == df->name) { message_at(msg_field_redefined, derived->source_file, 0, &bf->name, derived, this); break; } } } for (method_desc* bm = methods; bm != NULL; bm = bm->next) { if (bm->is_special_method() || (bm->attr & method_desc::m_static)) { continue; } for (method_desc* dm=derived->methods; dm != NULL; dm=dm->next) { if ( (bm->name == dm->name) && (bm->desc == dm->desc)) { if (!(dm->attr & method_desc::m_override)) { bm->overridden = new overridden_method(dm, bm->overridden); if ((bm->attr & method_desc::m_synchronized) && !(dm->attr & method_desc::m_synchronized)) { message_at(msg_nosync, derived->source_file, dm->first_line, bm, derived); } if (!(attr & cl_interface)) { dm->attr |= method_desc::m_override; } } break; } } } /* find possible override mistakes */ for (method_desc* bm = methods; bm != NULL; bm = bm->next) { if (bm->is_special_method() || (bm->attr & method_desc::m_static)) { continue; } bool overridden = false; method_desc* match = NULL; for (method_desc* dm=derived->methods; dm != NULL; dm=dm->next) { if (bm->name == dm->name) { if (!(dm->attr & method_desc::m_override)) match = dm; if (bm->desc == dm->desc) { overridden = true; break; } } } if (match != NULL && !overridden) { message_at(msg_not_overridden, derived->source_file, match->first_line, bm, derived); } } for (int n = 0; n < n_bases; n++) { bases[n]->check_inheritance(derived); } } void class_desc::calculate_attributes() { for (method_desc* method = methods; method != NULL; method = method->next){ method->calculate_attributes(); } } void class_desc::build_concurrent_closure() { for (method_desc* method = methods; method != NULL; method = method->next){ method->build_concurrent_closure(); } } void class_desc::verify() { for (method_desc* method = methods; method != NULL; method=method->next) { method->check_synchronization(); method->check_invocations(); } } void class_desc::build_class_info() { for (int i = 0; i < n_bases; i++) { bases[i]->check_inheritance(this); } for (method_desc* method = methods; method != NULL; method = method->next){ method->find_access_dependencies(); } bool overriden_equals = false; bool overriden_hashcode = false; method_desc* equals_hashcode_match = NULL; for (method_desc* bm = methods; bm != NULL; bm = bm->next) { if (bm->name == "equals") { overriden_equals = true; equals_hashcode_match = bm; } else if (bm->name == "hashCode") { overriden_hashcode = true; equals_hashcode_match = bm; } } if (overriden_equals != overriden_hashcode) { if (overriden_equals) { message_at(msg_hashcode_not_overridden, source_file, equals_hashcode_match->first_line); } else { message_at(msg_equals_not_overridden, source_file, equals_hashcode_match->first_line); } } } void class_desc::build_call_graph() { for (method_desc* method = methods; method != NULL; method = method->next){ method->build_call_graph(); } } void class_desc::global_analysis() { class_desc* cls; // // Set attributes which depends on inheritance hierarchy. // for (cls = chain; cls != NULL; cls = cls->next) { cls->calculate_attributes(); } // // Set m_concurrent attribute for all synchronized methods and methods // called from them. Detect invocation of wait() method with multiple // monitors locked. // for (cls = chain; cls != NULL; cls = cls->next) { cls->build_concurrent_closure(); } for (cls = chain; cls != NULL; cls = cls->next) { cls->build_class_info(); } for (cls = chain; cls != NULL; cls = cls->next) { cls->build_call_graph(); } // // Check non-synchrnized access to variables by concurrent methods // (race condition) and detect unchecked accessed to formal parameters // of method which can be passed "null" value in some invocation of // this method // for (cls = chain; cls != NULL; cls = cls->next) { cls->verify(); } // // Explore class dependency grpah to detect possible sources of // deadlocks // graph_vertex::verify(); } jlint-3.0/class_desc.hh0000644000175000017500000000273207242270225015664 0ustar davehodaveho00000000000000#ifndef CLASS_DESC_HH #define CLASS_DESC_HH #include "types.hh" #include "locks.hh" #include "utf_string.hh" #include "field_desc.hh" #include "graph.hh" #include "method_desc.hh" #include "overridden_method.hh" class class_desc { public: Locks locks; // locks held by current thread Locks usedLocks; // locks (other than "this") ever used by current class utf_string name; utf_string source_file; class_desc* next; class_desc* collision_chain; method_desc* methods; int attr; enum class_attrs { cl_interface = 0x00200, cl_system = 0x10000 }; int n_bases; class_desc** bases; field_desc* fields; graph_vertex* class_vertex; graph_vertex* metaclass_vertex; static class_desc* get(utf_string const& str); method_desc* get_method(utf_string const& mth_name, utf_string const& mth_desc); field_desc* get_field(utf_string const& field_name); static class_desc* hash_table[]; static int n_classes; static class_desc* chain; bool isa(const char* cls_name); bool isa(class_desc* cls); bool implements(const char* interface_name); bool in_relationship_with(class_desc* cls); void verify(); void calculate_attributes(); void build_class_info(); void build_call_graph(); void build_concurrent_closure(); void check_inheritance(class_desc* derived); static void global_analysis(); class_desc(utf_string const& str); }; #endif jlint-3.0/component_desc.hh0000644000175000017500000000055107234134153016556 0ustar davehodaveho00000000000000#ifndef COMPONENT_DESC_HH #define COMPONENT_DESC_HH class class_desc; #include "utf_string.hh" class component_desc { public: utf_string name; class_desc* cls; class_desc* accessor; component_desc(utf_string const& component_name, class_desc* component_cls) : name(component_name), cls(component_cls), accessor(NULL) {} }; #endif jlint-3.0/constant.hh0000644000175000017500000000413007240344547015413 0ustar davehodaveho00000000000000#ifndef CONSTANT_HH #define CONSTANT_HH #include "types.hh" #include "utf_string.hh" class constant { public: byte tag; virtual int length() = 0; virtual type_tag type() { return tp_object; } constant(byte* p) { tag = *p; } constant() { tag = 0; } // for is_this }; class const_class : public constant { public: int name; const_class(byte* p) : constant(p) { name = unpack2(p+1); } int length() { return 3; } }; class const_double : public constant { public: const_double(byte* p) : constant(p) {} int length() { return 9; } type_tag type() { return tp_double; } }; class const_float : public constant { public: const_float(byte* p) : constant(p) {} int length() { return 5; } type_tag type() { return tp_float; } }; class const_int : public constant { public: int value; const_int(byte* p) : constant(p) { value = unpack4(p+1); } int length() { return 5; } type_tag type() { return tp_int; } }; class const_long : public constant { public: struct { int4 high; int4 low; } value; const_long(byte* p) : constant(p) { value.high = unpack4(p+1); value.low = unpack4(p+5); } int length() { return 9; } type_tag type() { return tp_long; } }; class const_name_and_type : public constant { public: int name; int desc; const_name_and_type(byte* p) : constant(p) { name = unpack2(p+1); desc = unpack2(p+3); } const_name_and_type(int n, int t) { // used for is_this name = n; desc = t; } int length() { return 5; } }; class const_ref : public constant { public: int cls; int name_and_type; const_ref(byte* p) : constant(p) { cls = unpack2(p+1); name_and_type = unpack2(p+3); } int length() { return 5; } }; class const_string : public constant { public: int str; const_string(byte* p) : constant(p) { str = unpack2(p+1); } int length() { return 3; } type_tag type() { return tp_string; } }; class const_utf8 : public constant, public utf_string { public: const_utf8(byte* p) : constant(p), utf_string(unpack2(p+1), p+3) {} int length() { return 3 + len; } }; #endif jlint-3.0/field_desc.hh0000644000175000017500000000203007307147131015632 0ustar davehodaveho00000000000000#ifndef FIELD_DESC_HH #define FIELD_DESC_HH #ifdef VISUAL_CPP using namespace std; #pragma warning (disable : 4786) #endif #include "component_desc.hh" #include "utf_string.hh" class const_name_and_type; class class_desc; class field_desc : public component_desc { public: field_desc* next; int attr; enum { f_static = 0x0008, f_final = 0x0010, f_volatile = 0x0040, f_used = 0x10000, f_serialized = 0x20000 // field is accessed only from methods // of related classes }; const field_desc* equals; // value that has been assigned, or NULL == unknown const_name_and_type* name_and_type; // NULL == unknown vector writes; void write(int linenumber) { // mark field as written to writes.push_back(linenumber); } field_desc(utf_string const& field_name, class_desc* owner, field_desc* chain) : component_desc(field_name, owner) { equals = NULL; name_and_type = NULL; next = chain; attr = f_serialized; } }; #endif jlint-3.0/functions.hh0000644000175000017500000000300610001266370015555 0ustar davehodaveho00000000000000// extern function declarations #ifndef FUNCTIONS_HH #define FUNCTIONS_HH #include #include "types.hh" #include class utf_string; /* The following preprocessor directives are historically grown, if anyone wanted to clean up, I'd be very grateful :) */ #ifndef __VALIST #ifdef va_list #define __VALIST va_list #endif // FreeBSD uses typedef in stdarg.h #ifdef __FreeBSD__ #define __VALIST va_list #endif // so does OpenBSD #ifdef __OpenBSD__ #define __VALIST va_list #endif // so does LinuxPPC #ifdef __PPC__ #define __VALIST va_list #endif // so does MacOSX #ifdef __APPLE_CC__ #define __VALIST va_list #endif // so does MSVC++ #ifdef VISUAL_CPP #define __VALIST va_list #endif // so does alpha #ifdef __alpha__ #define __VALIST va_list #endif // so does GNUC #ifdef __GNUC__ #define __VALIST va_list #endif /* replaced by version above found in jlint for debian distro // Fix for g++3.2, not needed for g++3.0 or lower. g++3.1?? #ifdef __GNUC__ #if __GNUC__ > 2 #ifdef __GNUC_MINOR__ #if __GNUC_MINOR__ > 0 #define __VALIST va_list #endif #endif #endif #endif */ // Fix for cygwin (and possible others), if va_list typedef'd or undefined #ifndef __VALIST #define __VALIST void* #endif #endif extern void format_message(int code, utf_string const& file, int line, __VALIST ap); extern void message_at(int code, utf_string const& file, int line, ...); extern int get_type(utf_string const& str); extern int get_number_of_parameters(utf_string const& str); extern unsigned string_hash_function(byte* p); #endif jlint-3.0/graph.cc0000644000175000017500000000737207242270225014655 0ustar davehodaveho00000000000000#include "graph.hh" void graph_edge::message(int loop_id) { caller->print_call_path_to(invocation, loop_id, 0); class_desc* abstract_class = invocation->method->cls; invocation->method->cls = vertex->cls; if (!strcmp(invocation->method->name.as_asciz(), "")) { // special error message about synchronized blocks if (invocation->method->attr & method_desc::m_sync_block) { const char* lock1 = invocation->self_class->name.as_asciz(); // pretty print lock set string lockset = ""; monitor_stack::const_iterator entry = caller->locksAtEntry.begin(); while (entry != caller->locksAtEntry.end()) { if ((*entry)->cls == NULL) break; const char* full_name = compound_name((*entry)->cls->name.as_asciz(), (*entry)->name.as_asciz()); if (strcmp(full_name, lock1)) { lockset += string(", "); if (strcmp(full_name, "")) { lockset += full_name; } else { lockset += ""; } } entry++; } if (caller->locksAtEntry.nLocks() > 2) { lockset += "}"; lockset.replace(0, 2, "set {"); } else { lockset.replace(0, 2, ""); } if (lockset == "") { lockset = ""; } invocation->message(msg_lock, lock1, lockset.c_str()); } else { invocation->message(msg_lock, invocation->self_class->name.as_asciz(), caller->cls->name.as_asciz()); } } else { if (!(invocation->method->attr & method_desc::m_sync_block)) { invocation->message(msg_sync_loop, (void*)loop_id, invocation->method); } } invocation->method->cls = abstract_class; mask |= (1 << (loop_id & 31)); } void graph_vertex::verify() { graph_edge** stack = new graph_edge*[n_vertexes]; int marker = 0; int n_loops = 0; for (graph_vertex* root = graph; root != NULL; root = root->next) { if (root->marker <= marker || root->visited >= max_shown_paths) { continue; } int sp = 0; int i; graph_edge* edge = root->edges; root->visited |= flag_vertex_on_path; root->marker = ++marker; while (edge != NULL) { while (edge->vertex->marker >= marker || edge->vertex->visited < max_shown_paths) { graph_vertex* vertex = edge->vertex; stack[sp++] = edge; if (vertex->visited & flag_vertex_on_path) { // we found loop in the graph unsigned mask = edge->mask; for (i = sp-1; --i >= 0 && stack[i]->vertex != vertex;) { mask &= stack[i]->mask; } if (mask == 0) { n_loops += 1; while (++i < sp) { stack[i]->message(n_loops); } } } else { // Pass through the graph in depth first order if (vertex->edges != NULL) { vertex->visited = (vertex->visited + 1) | flag_vertex_on_path; vertex->marker = marker; vertex->n_loops = n_loops; edge = vertex->edges; continue; } } sp -= 1; break; } // end of depth-first loop while (edge->next == NULL) { if (--sp < 0) break; edge = stack[sp]; edge->vertex->visited &= ~flag_vertex_on_path; if (edge->vertex->n_loops == n_loops) { // This path doesn't lead to deadlock: avoid future // visits of this vertex edge->vertex->marker = 0; } } edge = edge->next; } root->visited &= ~flag_vertex_on_path; } } jlint-3.0/graph.hh0000644000175000017500000000212507236032035014654 0ustar davehodaveho00000000000000#ifndef GRAPH_HH #define GRAPH_HH #include "types.hh" #include "method_desc.hh" class callee_desc; class graph_vertex; class graph_edge { public: graph_edge* next; callee_desc* invocation; method_desc* caller; graph_vertex* vertex; int mask; void message(int loop_id); graph_edge(graph_vertex* node, method_desc* method, callee_desc* call) { invocation = call; caller = method; vertex = node; mask = 0; } }; class graph_vertex { public: graph_edge* edges; graph_vertex* next; class_desc* cls; int visited; int marker; int n_loops; enum { flag_vertex_on_path = 0x80000000, flag_vertex_not_marked = 0x7fffffff }; static int n_vertexes; static graph_vertex* graph; static void verify(); void attach(graph_edge* edge) { edge->next = edges; edges = edge; } graph_vertex(class_desc* vertex_class) { cls = vertex_class; visited = 0; marker = flag_vertex_not_marked; next = graph; graph = this; edges = NULL; n_vertexes += 1; } }; #endif jlint-3.0/inlines.hh0000644000175000017500000001671307653162217015235 0ustar davehodaveho00000000000000// inline functions #ifndef INLINES_HH #define INLINES_HH #include inline int unpack2(byte* s) { return (s[0] << 8) + s[1]; } inline int unpack4(byte* s) { return (((((s[0] << 8) + s[1]) << 8) + s[2]) << 8) + s[3]; } inline int unpack2_le(byte* s) { return (s[1] << 8) + s[0]; } inline int unpack4_le(byte* s) { return (((((s[3] << 8) + s[2]) << 8) + s[1]) << 8) + s[0]; } // // Some bit support functions // inline int first_set_bit(int4 val) { if (val == 0) { return 32; } int n = 0; while (!(val & 1)) { n += 1; val >>= 1; } return n; } inline int last_set_bit(nat4 val) { int n = 0; if (val == 0) { return -1; } while ((val >>= 1) != 0) { n += 1; } return n; } inline int minimum(int4 x, int4 y) { return x < y ? x : y; } inline int maximum(int4 x, int4 y) { return x > y ? x : y; } inline int4 make_mask(int shift) { return shift < 0 ? ALL_BITS : shift >= 32 ? 0 : ALL_BITS << shift; } inline int4 make_mask(int high_bit, int low_bit) { int4 mask = (high_bit >= 31) ? ALL_BITS : ((1 << (high_bit+1)) - 1); if (low_bit < 32) mask &= ~((1 << low_bit) - 1); return mask; } inline int4 make_lshift_mask(int4 mask, int min_shift, int max_shift) { if (unsigned(max_shift - min_shift) >= 32) { return ALL_BITS; } int4 result = 0; while (min_shift <= max_shift) { result |= mask << (min_shift & 31); min_shift += 1; } return result; } inline int4 make_rshift_mask(int4 mask, int min_shift, int max_shift) { if (unsigned(max_shift - min_shift) >= 32) { return ALL_BITS; } int4 result = 0; while (min_shift <= max_shift) { result |= mask >> (min_shift & 31); min_shift += 1; } return result; } inline int4 make_rushift_mask(nat4 mask, int min_shift, int max_shift) { if (unsigned(max_shift - min_shift) >= 32) { return ALL_BITS; } int4 result = 0; while (min_shift <= max_shift) { result |= mask >> (min_shift & 31); min_shift += 1; } return result; } inline bool calculate_multiply_range(vbm_operand& z, vbm_operand& x, vbm_operand& y) { // z = x*y if (x.max < 0 && y.max < 0) { z.min = x.max*y.max; z.max = x.min*y.min; return (z.max > 0 && z.max/x.min == y.min);// no overflow } else if (x.max < 0 && y.min < 0 && y.max >= 0) { z.min = x.min*y.max; z.max = x.min*y.min; return (z.max > 0 && z.min/x.min == y.max && z.max/x.min == y.min); } else if (x.max < 0 && y.min >= 0) { z.min = x.min*y.max; z.max = x.max*y.min; return (z.min/x.min == y.max && z.max/x.max == y.min); // no overflow } else if (x.min < 0 && x.max >= 0 && y.max < 0) { z.min = x.max*y.min; z.max = x.min*y.min; return (z.max > 0 && z.min/y.min == x.max && z.max/y.min == x.min); } else if (x.min < 0 && x.max >= 0 && y.min < 0 && y.max >= 0) { int4 m1, m2; m1 = x.min*y.max; m2 = x.max*y.min; if (m1/x.min != y.max || m2/y.min != x.max) return false; z.min = minimum(m1, m2); m1 = x.max*y.max; m2 = x.min*y.min; if (m2 <= 0 || (x.max != 0 && m1/x.max != y.max) || m2/y.min != x.min) return false; z.max = maximum(m1, m2); return true; } else if (x.min < 0 && x.max >= 0 && y.min >= 0) { z.min = x.min*y.max; z.max = x.max*y.max; return z.min/x.min == y.max && (x.max == 0 || z.max/x.max == y.max); } else if (x.min >= 0 && y.max < 0) { z.min = x.max*y.min; z.max = x.min*y.max; return z.min/y.min == x.max && z.max/y.max == x.min; } else if (x.min >= 0 && y.min < 0 && y.max >= 0) { z.min = x.max*y.min; z.max = x.max*y.max; return z.min/y.min == x.max && (x.max == 0 || z.max/x.max == y.max); } else { assert(x.min >= 0 && y.min >= 0); z.min = x.min*y.min; z.max = x.max*y.max; return x.max == 0 || z.max/x.max == y.max; } } #ifdef INT8_DEFINED inline int first_set_bit(int8 val) { if (val == 0) { return 64; } int n = 0; while (!(val & 1)) { n += 1; val >>= 1; } return n; } inline int8 make_int8_mask(int shift) { return shift < 0 ? INT8_ALL_BITS : shift >= 64 ? INT8_ZERO : INT8_ALL_BITS << shift; } inline int8 make_int8_lshift_mask(int8 mask, int min_shift, int max_shift) { if (unsigned(max_shift - min_shift) >= 64) { return INT8_ALL_BITS; } int8 result = 0; while (min_shift <= max_shift) { result |= mask << (min_shift & 63); min_shift += 1; } return result; } inline int8 make_int8_rshift_mask(int8 mask, int min_shift, int max_shift) { if (unsigned(max_shift - min_shift) >= 64) { return INT8_ALL_BITS; } int8 result = 0; while (min_shift <= max_shift) { result |= mask >> (min_shift & 63); min_shift += 1; } return result; } inline int8 make_int8_rushift_mask(nat8 mask, int min_shift, int max_shift) { if (unsigned(max_shift - min_shift) >= 64) { return INT8_ALL_BITS; } int8 result = 0; while (min_shift <= max_shift) { result |= mask >> (min_shift & 63); min_shift += 1; } return result; } inline bool calculate_int8_multiply_range(vbm_operand* x, vbm_operand* y) { // x *= y int8 x_min = LOAD_INT8(x, min); int8 x_max = LOAD_INT8(x, max); int8 y_min = LOAD_INT8(y, min); int8 y_max = LOAD_INT8(y, max); int8 z_min, z_max; if (x_max < 0 && y_max < 0) { z_min = x_max*y_max; z_max = x_min*y_min; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return (z_min > 0 && z_max/x_min == y_min); // no overflow } else if (x_max < 0 && y_min < 0 && y_max >= 0) { z_min = x_min*y_max; z_max = x_min*y_min; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return (z_max > 0 && z_min/x_min == y_max && z_max/x_min == y_min); } else if (x_max < 0 && y_min >= 0) { z_min = x_min*y_max; z_max = x_max*y_min; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return (z_min/x_min == y_max && z_max/x_max == y_min); // no overflow } else if (x_min < 0 && x_max >= 0 && y_max < 0) { z_min = x_max*y_min; z_max = x_min*y_min; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return (z_max > 0 && z_min/y_min == x_max && z_max/y_min == x_min); } else if (x_min < 0 && x_max >= 0 && y_min < 0 && y_max >= 0) { int8 m1, m2; m1 = x_min*y_max; m2 = x_max*y_min; if (m1/x_min != y_max || m2/y_min != x_max) return false; z_min = m1 < m2 ? m1 : m2; m1 = x_max*y_max; m2 = x_min*y_min; if (m2 <= 0 || (x_max != 0 && m1/x_max != y_max) || m2/y_min != x_min) return false; z_max = m1 > m2 ? m1 : m2; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return true; } else if (x_min < 0 && x_max >= 0 && y_min >= 0) { z_min = x_min*y_max; z_max = x_max*y_max; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return z_min/x_min == y_max && (x_max == 0 || z_max/x_max == y_max); } else if (x_min >= 0 && y_max < 0) { z_min = x_max*y_min; z_max = x_min*y_max; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return z_min/y_min == x_max && z_max/y_max == x_min; } else if (x_min >= 0 && y_min < 0 && y_max >= 0) { z_min = x_max*y_min; z_max = x_max*y_max; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return z_min/y_min == x_max && (x_max == 0 || z_max/x_max == y_max); } else { assert(x_min >= 0 && y_min >= 0); z_min = x_min*y_min; z_max = x_max*y_max; STORE_INT8(x, min, z_min); STORE_INT8(x, max, z_max); return x_max == 0 || z_max/x_max == y_max; } } #endif #endif jlint-3.0/jlint.cc0000644000175000017500000010474010001266370014662 0ustar davehodaveho00000000000000//-< JLINT.CC >------------------------------------------------------+--------+ // Jlint Version 3.0 (c) 1998 GARRET | ? | // (Java Lint) | /\| | // | / \ | // Created: 28-Mar-98 K.A. Knizhnik | / [] \ | // Version 2.X: Last update: 05-Jun-01 Cyrille Artho | GARRET | // Version 3.X: Last update: 20-Aug-03 Raphael Ackermann | | //-------------------------------------------------------------------+--------+ // Java verifier //-------------------------------------------------------------------+--------+ #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include "jlint.hh" extern "C" { #include // should I use or "zlib.h" ?? // #include "zlib.h" }; bool verbose = false; int max_shown_paths = 4; int n_messages; char* source_file_path; int source_file_path_len; bool source_path_redefined = false; int reported_message_mask = cat_all; FILE* history; string_pool stringPool; field_desc* is_const; message_descriptor msg_table[] = { #define MSG(category, code, position_dependent, format) \ {cat_##category, MSG_LOCATION_PREFIX format, #code, position_dependent, true}, #include "jlint.msg" {cat_all} }; unsigned int string_hash_function(byte* p) { unsigned int h = 0, g; while(*p) { h = (h << 4) + *p++; if ((g = h & 0xF0000000) != 0) { h ^= g >> 24; } h &= ~g; } return h; } // // Functions for extracting information from type descriptors // void message_at(int code, utf_string const& file, int line, ...) { va_list ap; va_start(ap, line); format_message(code, file, line, ap); va_end(ap); } int get_number_of_parameters(utf_string const& str) { const char* p = str.as_asciz(); assert(*p++ =='('); int n_params = 0; while (*p != ')') { switch (*p++) { case '[': while (*p == '[') p += 1; if (*p++ == 'L') { while (*p++ != ';'); } n_params += 1; break; case 'D': case 'J': n_params += 2; break; case 'L': while (*p++ != ';'); nobreak; default: n_params += 1; } } return n_params; } int get_type(utf_string const& str) { const char* p = str.as_asciz(); if (*p == '(') { while (*++p != ')'); p += 1; } int indirect = 0; while (*p == '[') { p += 1; indirect += 1; } type_tag tag = tp_object; switch (*p) { case 'I': tag = tp_int; break; case 'S': tag = tp_short; break; case 'D': tag = tp_double; break; case 'J': tag = tp_long; break; case 'F': tag = tp_float; break; case 'V': tag = tp_void; break; case 'B': tag = tp_byte; break; case 'C': tag = tp_char; break; case 'Z': tag = tp_bool; break; default: if (strcmp(p, "Ljava/lang/String;") == 0) tag = tp_string; } return int(tag) + (indirect << 8); } // // All messages are reported by this function // void format_message(int code, utf_string const& file, int line, __VALIST ap) { static int loop_id; static message_node *first, *last; static char* compound_message; const void* parameter[MAX_MSG_PARAMETERS]; int n_parameters = 2; if (code == msg_loop || code == msg_sync_loop) { // extract loop identifier parameter[n_parameters++] = va_arg(ap, void*); } if (history != NULL) { if (compound_message != NULL && ((loop_id != 0 && ((code != msg_loop && code != msg_sync_loop) || (int)parameter[2] != loop_id)) || (loop_id == 0 && code != msg_wait_path))) { if (!message_node::find(compound_message)) { message_node *msg = first, *next; do { next = msg->next; fprintf(stdout, "%s\n", msg->text); delete msg; n_messages += 1; } while ((msg = next) != NULL); fprintf(history, "%s\n", compound_message); } delete[] compound_message; compound_message = NULL; } } if ((reported_message_mask & msg_table[code].category) && msg_table[code].enabled) { static int prev_line = 0; char msg_buf[MAX_MSG_LENGTH]; char his_buf[MAX_MSG_LENGTH]; char* hp = his_buf; if (line == 0) { line = ++prev_line; // avoid several messages with 0 line number } else { prev_line = 0; } parameter[0] = file.as_asciz(); parameter[1] = (void*)line; if (history) { hp += sprintf(hp, "%s", msg_table[code].name); } char const* src = msg_table[code].format; char* dst = msg_buf; if (code == msg_done) { // do not output location prefix for termination message src += strlen(MSG_LOCATION_PREFIX); parameter[0] = (void*)n_messages; } while (*src != 0) { if (*src == '%') { int index; int pos; int n = sscanf(++src, "%d%n", &index, &pos); assert(n == 1); assert(index < MAX_MSG_PARAMETERS); while (index >= n_parameters) { parameter[n_parameters++] = va_arg(ap, void*); } src += pos; char* save_dst = dst; switch (*src++) { case 's': // zero terminated string dst += sprintf(dst, "%s", (char*)parameter[index]); break; case 'm': // method descriptor dst += ((method_desc*)parameter[index])-> demangle_method_name(dst); break; case 'u': // utf8 string dst += sprintf(dst, "%s", ((utf_string*)parameter[index])->as_asciz()); break; case 'c': // class descriptor dst += sprintf(dst, "%s", ((class_desc*)parameter[index])-> name.as_asciz()); break; case 'd': // integer dst += sprintf(dst, "%d", (int)parameter[index]); break; default: assert(false/*bad message parameter format*/); } if (history) { // append parameeter to history buffer if ((index >= 2 || msg_table[code].position_dependent) && (code != msg_sync_loop || index > 2) && (code != msg_loop || index > 3)) { // Do not inlude loop number in history message hp += sprintf(hp, ":%.*s", dst - save_dst, save_dst); } } } else { *dst++ = *src++; } } *dst++ = '.'; *dst = 0; if (history != NULL) { if (compound_message != NULL) { char* new_msg = new char[strlen(compound_message) +strlen(his_buf)+2]; sprintf(new_msg, "%s;%s", compound_message, his_buf); last = last->next = new message_node(msg_buf); delete[] compound_message; compound_message = new_msg; } else { if (code == msg_loop || code == msg_sync_loop || code == msg_wait) { compound_message = strdup(his_buf); first = last = new message_node(msg_buf); if (code != msg_wait) { loop_id = (int)parameter[2]; } } else if (!message_node::find(his_buf)) { fprintf(stdout, "%s\n", msg_buf); if (code != msg_done) { fprintf(history, "%s\n", his_buf); n_messages += 1; } } } } else { fprintf(stdout, "%s\n", msg_buf); if (code != msg_done) { n_messages += 1; } } } } graph_vertex* graph_vertex::graph; int graph_vertex::n_vertexes; // // Methods of class_desc class // class_desc* class_desc::hash_table[class_hash_table_size]; class_desc* class_desc::chain; int class_desc::n_classes; void set_class_source_path(class_desc* cls) { if (source_file_path_len != 0) { const char* class_file_name = cls->source_file.as_asciz(); if (!source_path_redefined) { char* dirend = strrchr(class_file_name, '/'); if (dirend != NULL) { int dirlen = dirend - class_file_name; if (dirlen <= source_file_path_len && memcmp(class_file_name, source_file_path + source_file_path_len - dirlen, dirlen) == 0) { class_file_name = dirend+1; } } } char file_name_buf[MAX_MSG_LENGTH]; int len = sprintf(file_name_buf, "%.*s%c%s", source_file_path_len, source_file_path, FILE_SEP, class_file_name); cls->source_file = utf_string(len, (byte*)file_name_buf); } } bool parse_class_file(byte* fp) { int i; unsigned magic = unpack4(fp); fp += 4; if (magic != 0xCAFEBABE) return false; int minor_version = unpack2(fp); fp += 2; int major_version = unpack2(fp); fp += 2; int constant_pool_count = unpack2(fp); fp += 2; constant** constant_pool = new constant*[constant_pool_count]; memset(constant_pool, 0, sizeof(constant*)*constant_pool_count); int name_index = 0; for (i = 1; i < constant_pool_count; i++) { constant* cp = NULL; int n_extra_cells = 0; switch (*fp) { case c_utf8: cp = new const_utf8(fp); if (!strcmp(((const_utf8*)cp)->as_asciz(), "this")) { name_index = i; } break; case c_integer: cp = new const_int(fp); break; case c_float: cp = new const_float(fp); break; case c_long: cp = new const_long(fp); n_extra_cells += 1; break; case c_double: cp = new const_double(fp); n_extra_cells += 1; break; case c_class: cp = new const_class(fp); break; case c_string: cp = new const_string(fp); break; case c_field_ref: case c_method_ref: case c_interface_method_ref: cp = new const_ref(fp); break; case c_name_and_type: cp = new const_name_and_type(fp); break; } assert(cp != NULL); fp += cp->length(); constant_pool[i] = cp; i += n_extra_cells; } int access_flags = unpack2(fp); fp += 2; int this_class_name = unpack2(fp); fp += 2; int super_class_name = unpack2(fp); fp += 2; int interfaces_count = unpack2(fp); fp += 2; class_desc* this_class = class_desc::get(*(const_utf8*)constant_pool [((const_class*)constant_pool[this_class_name])->name]); set_class_source_path(this_class); // init. is_this field_desc* is_this = new field_desc(utf_string(""), this_class, NULL); // assign name_and_type for is_this - find name entry "this" and obj. type is_this->name_and_type = new const_name_and_type(name_index, ((const_class*)constant_pool[this_class_name])->name); is_this->equals = is_this; is_this->cls = this_class; // init. is_const field_desc* is_const = new field_desc(utf_string(""), NULL, NULL); this_class->attr = access_flags; if (super_class_name == 0) { // Object class assert(interfaces_count == 0); this_class->n_bases = 0; } else { this_class->n_bases = interfaces_count + 1; this_class->bases = new class_desc*[interfaces_count + 1]; this_class->bases[0] = class_desc::get(*(const_utf8*)constant_pool [((const_class*)constant_pool[super_class_name])->name]); for (i = 1; i <= interfaces_count; i++) { int interface_name = unpack2(fp); fp += 2; this_class->bases[i] = class_desc::get(*(const_utf8*)constant_pool [((const_class*)constant_pool[interface_name])->name]); } } int fields_count = unpack2(fp); fp += 2; while (--fields_count >= 0) { int access_flags = unpack2(fp); fp += 2; int name_index = unpack2(fp); fp += 2; int desc_index = unpack2(fp); fp += 2; int attr_count = unpack2(fp); fp += 2; field_desc* field = this_class->get_field(*(const_utf8*)constant_pool[name_index]); field->attr |= access_flags; while (--attr_count >= 0) { int attr_len = unpack4(fp+2); fp += 6 + attr_len; } } int methods_count = unpack2(fp); fp += 2; // // We need "SourceFile" attribute first, so remember position and // skip methods definitions // byte* method_part = fp; for (i = 0; i < methods_count; i++) { int attr_count = unpack2(fp+6); fp += 8; while (--attr_count >= 0) { int attr_len = unpack4(fp+2); fp += 6 + attr_len; } } int attr_count = unpack2(fp); fp += 2; while (--attr_count >= 0) { int attr_name = unpack2(fp); fp += 2; int attr_len = unpack4(fp); fp += 4; utf_string attr(*(const_utf8*)constant_pool[attr_name]); if (attr == "SourceFile") { int source_name = unpack2(fp); fp += 2; int file_name_idx = this_class->source_file.rindex(FILE_SEP); utf_string* file_name = (const_utf8*)constant_pool[source_name]; if (file_name_idx >= 0) { this_class->source_file.append(file_name_idx+1, *file_name); } else { this_class->source_file = *file_name; } } else { fp += attr_len; } } fp = method_part; while (--methods_count >= 0) { int access_flags = unpack2(fp); fp += 2; int name_index = unpack2(fp); fp += 2; int desc_index = unpack2(fp); fp += 2; int attr_count = unpack2(fp); fp += 2; const_utf8* mth_name = (const_utf8*)constant_pool[name_index]; const_utf8* mth_desc = (const_utf8*)constant_pool[desc_index]; method_desc* method = this_class->get_method(*mth_name, *mth_desc); method->attr |= access_flags; method->vars = NULL; while (--attr_count >= 0) { int attr_name = unpack2(fp); fp += 2; int attr_len = unpack4(fp); fp += 4; utf_string attr(*(const_utf8*)constant_pool[attr_name]); if (attr == "Code") { int max_stack = unpack2(fp); fp += 2; int max_locals = unpack2(fp); fp += 2; int code_length = unpack4(fp); fp += 4; method->code = fp; method->code_length = code_length; fp += code_length; method->n_vars = max_locals; method->vars = max_locals > 0 ? new var_desc[max_locals] : NULL; method->line_table = new word[code_length]; memset(method->line_table, 0, sizeof(word)*code_length); method->context = new local_context*[code_length+1]; memset(method->context, 0, sizeof(local_context*)*(code_length+1)); int exception_table_length = unpack2(fp); fp += 2; /* add new entry for each distinct "byte code adress of handle". ** ** if an exception handler at byte code "pos" handles exception of ** more than one byte code range, call ** "new ctx_entry_point(&method->context[pos]);" only once! Because ** otherwise the stack gets out of control. ** ** in the following example there are two different handle adresses ** 16 and 25. and for each of them ** "new ctx_entry_point(&method->context[handler_pc]);" is called ** exactly once. Therefore the program calls : ** new ctx_entry_point(&method->context[16]); ** new ctx_entry_point(&method->context[25]); ********************************************************************** ** Example Exception Table: ** ** ------------------------------------- ** ** ** ** byte code adress ** ** from to of handle ** ** 2 10 16 ** ** 12 14 16 ** ** 20 23 25 ** ********************************************************************** ** ** it is expected that the byte code adresses of the handles are ** ordered. If this would not be the case, a simple comparison of ** handler_pc and old_handler_pc would not be sufficient! */ int old_handler_pc = -1; while (--exception_table_length >= 0) { int handler_pc = unpack2(fp+4); if ( handler_pc != old_handler_pc) { new ctx_entry_point(&method->context[handler_pc]); } fp += 8; old_handler_pc = handler_pc; } int method_attr_count = unpack2(fp); fp += 2; bool line_table_present = false; bool local_var_table_present = false; while (--method_attr_count >= 0) { int mth_attr_name = unpack2(fp); fp += 2; int mth_attr_len = unpack4(fp); fp += 4; utf_string* attr = (const_utf8*)constant_pool[mth_attr_name]; if (*attr == "LineNumberTable") { int table_length = unpack2(fp); fp += 2; while (--table_length >= 0) { int start_pc = unpack2(fp); fp += 2; int line_no = unpack2(fp); fp += 2; method->line_table[start_pc] = line_no; } method->first_line = method->line_table[0]; if (method->first_line != 0) method->first_line -= 1; line_table_present = true; } else if (*attr == "LocalVariableTable") { method->local_variable_table_present = true; int table_length = unpack2(fp); fp += 2; while (--table_length >= 0) { int start_pc = unpack2(fp); fp += 2; int length = unpack2(fp); fp += 2; int name = unpack2(fp); fp += 2; int desc = unpack2(fp); fp += 2; int index = unpack2(fp); fp += 2; int type = (index == 0 && !(access_flags & method_desc::m_static)) ? tp_self : get_type(*(const_utf8*)constant_pool[desc]); new ctx_push_var(&method->context[start_pc], (const_utf8*)constant_pool[name], type, index, start_pc); new ctx_pop_var(&method->context[start_pc+length], index); } local_var_table_present = true; } else { fp += mth_attr_len; } } if (verbose && !(access_flags & method_desc::m_native)) { char buf[MAX_MSG_LENGTH]; method->demangle_method_name(buf); if (!line_table_present) { fprintf(stderr, "No line table present " "for method %s\n", buf); } if (!local_var_table_present) { fprintf(stderr, "No local variable table present " "for method %s\n", buf); } } method->parse_code(constant_pool, is_this); } else { fp += attr_len; } } } for (i = 1; i < constant_pool_count; i++) { if (constant_pool[i] != NULL) delete constant_pool[i]; } /* for (method_desc* m = this_class->methods; m != NULL; m = m->next) { m->locksAtEntry.clear(); } */ delete[] constant_pool; delete is_this->name_and_type; //delete is_this; delete is_const; monitor_stack::const_iterator it; for (it = this_class->usedLocks.begin(); it != this_class->usedLocks.end(); it++) { if (!((*it)->writes.empty())) { vector::const_iterator i; for (i = (*it)->writes.begin(); i != (*it)->writes.end(); i++) { message_at(msg_lock_assign, this_class->source_file, *i, *it); // *i: line number; *it = field_desc* } } } return true; } // // Determine type of file and extract Java class description from the file // inline int stricmp(const char* p, const char* q) { do { while (*p == '_') p += 1; // ignore '_' while (*q == '_') q += 1; // ignore '_' int diff = toupper(*(byte*)p) - toupper(*(byte*)q); if (diff != 0) { return diff; } q += 1; } while (*p++ != 0); return 0; } void proceed_file(char* file_name, bool recursive = false) { #ifdef _WIN32 HANDLE dir; char dir_path[MAX_PATH]; WIN32_FIND_DATA file_data; if (recursive != 0) { sprintf(dir_path, "%s\\*", file_name); if ((dir=FindFirstFile(dir_path, &file_data)) != INVALID_HANDLE_VALUE) { file_name = dir_path; } } else { if (strcmp(file_name, "..") == 0 || strcmp(file_name, ".") == 0) { proceed_file(file_name, 1); return; } if ((dir=FindFirstFile(file_name, &file_data)) == INVALID_HANDLE_VALUE) { fprintf(stderr, "Failed to open file '%s'\n", file_name); return; } } if (dir != INVALID_HANDLE_VALUE) { do { if (!recursive || *file_data.cFileName != '.') { char file_path[MAX_PATH]; char* file_dir = strrchr(file_name, '\\'); char* file_name_with_path; if (file_dir != NULL) { int dir_len = file_dir - file_name + 1; memcpy(file_path, file_name, dir_len); strcpy(file_path+dir_len, file_data.cFileName); file_name_with_path = file_path; } else { file_name_with_path = file_data.cFileName; } proceed_file(file_name_with_path, recursive+1); } } while (FindNextFile(dir, &file_data)); FindClose(dir); return; } #else DIR* dir = opendir(file_name); if (dir != NULL) { struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (*entry->d_name != '.') { char full_name[MAX_PATH]; sprintf(full_name, "%s/%s", file_name, entry->d_name); proceed_file(full_name, 2); } } closedir(dir); return; } #endif if (recursive >= 2) { int len = strlen(file_name); if (len < 6 || stricmp(file_name + len - 6, ".class") != 0) { return; } } FILE* f = fopen(file_name, "rb"); if (f == NULL) { fprintf(stderr, "Failed to open file '%s'\n", file_name); return; } char* suf = file_name + strlen(file_name) - 4; if (suf > file_name && (stricmp(suf,".zip")==0 || stricmp(suf,".jar")==0)){ // extract files from ZIP archive byte hdr[ECREC_SIZE+4]; if (fseek(f, 0, SEEK_END) != 0 || ftell(f) < ECREC_SIZE+4 || fseek(f, -(ECREC_SIZE+4), SEEK_CUR) != 0 || fread(hdr, ECREC_SIZE+4, 1, f) != 1) { fprintf(stderr, "Bad format of ZIP file '%s'\n", file_name); return; } int count = unpack2_le(&hdr[TOTAL_ENTRIES_CENTRAL_DIR]); int dir_size = unpack4_le(&hdr[SIZE_CENTRAL_DIRECTORY]); byte* directory = new byte[dir_size]; if (fseek(f, -(dir_size+ECREC_SIZE+4), SEEK_CUR) != 0 || fread(directory, dir_size, 1, f) != 1) { fprintf(stderr, "Bad format of ZIP file '%s'\n", file_name); delete[] directory; return; } byte* dp = directory; while (--count >= 0) { int compression_method = unpack2_le(&dp[4+C_COMPRESSION_METHOD]); int compressed_size = unpack4_le(&dp[4+C_COMPRESSED_SIZE]); int uncompressed_size = unpack4_le(&dp[4+C_UNCOMPRESSED_SIZE]); int filename_length = unpack2_le(&dp[4+C_FILENAME_LENGTH]); int cextra_length = unpack2_le(&dp[4+C_EXTRA_FIELD_LENGTH]); if ((dp-directory)+filename_length+CREC_SIZE+4 > dir_size) { fprintf(stderr, "Bad format of ZIP file '%s'\n", file_name); break; } byte local_rec_buffer[LREC_SIZE+4]; int local_header_offset = unpack4_le(&dp[4+C_RELATIVE_OFFSET_LOCAL_HEADER]); if (fseek(f, local_header_offset, SEEK_SET) != 0 || fread(local_rec_buffer, sizeof local_rec_buffer, 1, f) != 1 || memcmp(&local_rec_buffer[1], LOCAL_HDR_SIG, 3) != 0) { fprintf(stderr, "Bad format of ZIP file '%s'\n", file_name); break; } int file_start = local_header_offset + (LREC_SIZE+4) + unpack2_le(&local_rec_buffer[4+L_FILENAME_LENGTH]) + unpack2_le(&local_rec_buffer[4+L_EXTRA_FIELD_LENGTH]); int filename_offset = CREC_SIZE+4; if (compression_method == C_UNCOMPRESSED) { if (uncompressed_size != 0) { byte* buffer = new byte[uncompressed_size]; if (fseek(f, file_start, SEEK_SET) != 0 || fread(buffer, uncompressed_size, 1, f) != 1) { fprintf(stderr, "Failed to read ZIP file '%s'\n", file_name); return; } else { if (verbose) { fprintf(stderr, "Verify file '%.*s'\n", filename_length, dp + filename_offset); } if (!parse_class_file(buffer)) { fprintf(stderr, "File '%.*s' isn't correct Java class " "file\n", filename_length, dp+filename_offset); } } delete[] buffer; } } else if (compression_method == C_DEFLATE) { if ((uncompressed_size != 0) && (compressed_size != 0)) { byte* uncbuffer = new byte[uncompressed_size]; byte* cbuffer = new byte[compressed_size]; if (fseek(f, file_start, SEEK_SET) != 0 || fread(cbuffer, compressed_size, 1, f) != 1) { fprintf(stderr, "Failed to read ZIP file '%s'\n", file_name); return; } else { z_stream c_stream; c_stream.zalloc = (alloc_func) 0; c_stream.zfree = (free_func)0; c_stream.opaque = (voidpf)0; int err = inflateInit2(&c_stream, -MAX_WBITS); if (err != Z_OK) { fprintf(stderr, "Error %s in inflateInit while uncompressing %.*s.\n", c_stream.msg, filename_length, dp+filename_offset); } else { c_stream.next_in = cbuffer; c_stream.next_out = uncbuffer; c_stream.avail_in = compressed_size; c_stream.avail_out = uncompressed_size; c_stream.total_in = 0; c_stream.total_out = 0; while ((err=inflate(&c_stream, Z_SYNC_FLUSH)) == Z_OK); if ((err != Z_STREAM_END) && (err != Z_OK)) { fprintf(stderr, "Error %s in inflate while uncompressing %.*s.\n", c_stream.msg, filename_length, dp+filename_offset); } else { inflateEnd(&c_stream); if (verbose) { fprintf(stderr, "Verify file '%.*s'\n", filename_length, dp + filename_offset); } if (!parse_class_file(uncbuffer)) { fprintf(stderr, "File '%.*s' isn't correct Java class " "file\n", filename_length, dp+filename_offset); } } } } delete[] cbuffer; delete[] uncbuffer; } } dp += filename_offset + filename_length + cextra_length; } delete[] directory; } else { fseek(f, 0, SEEK_END); size_t file_size = ftell(f); fseek(f, 0, SEEK_SET); byte* buffer = new byte[file_size]; if (fread(buffer, file_size, 1, f) != 1) { fprintf(stderr, "Failed to read file '%s'\n", file_name); } else { if (verbose) { fprintf(stderr, "Verify file '%s'\n", file_name); } if (!parse_class_file(buffer)) { fprintf(stderr, "File '%s' isn't correct Java class file\n", file_name); } } delete[] buffer; } fclose(f); } // // Main function: parse command line // msg_select_category_option msg_category_option[] = { {cat_synchronization, "synchronization", "detect synchronizational problems"}, {cat_deadlock, "deadlock", "possible deadlock detection"}, {cat_race_condition, "race_condition", "possible race condition detection"}, {cat_wait_nosync, "wait_nosync", "wait or notify is called from non-synchronized method"}, {cat_inheritance, "inheritance", "detect problems with class/interface inheritance"}, {cat_super_finalize, "super_finalize", "super.finalize() is not called from finalize method"}, {cat_not_overridden, "not_overridden", "methods with the same names and different profiles"}, {cat_field_redefined, "field_redefined", "instance or static variable is redefined in derived class"}, {cat_shadow_local, "shadow_local", "local variable shadows one in exterior scope"}, {cat_data_flow, "data_flow", "perform data flow analysis to detect suspicious operations"}, {cat_null_reference, "null_reference", "detect access to variables with possible NULL value"}, {cat_zero_operand, "zero_operand", "one of the operands of a binary integer operation is zero"}, {cat_zero_result, "zero_result", "result of operation is always zero"}, {cat_redundant, "redundant", "operation always produces the same result"}, {cat_overflow, "overflow", "detect incorrect handling of operation overflow"}, {cat_incomp_case, "incomp_case", "switch case label can't be produced by switch expression"}, {cat_short_char_cmp, "short_char_cmp", "expression of char type is compared with one of short type"}, {cat_string_cmp, "string_cmp", "two strings are compared as object references"}, {cat_weak_cmp, "weak_cmp", "using of inequality comparison where equality can be checked"}, {cat_domain, "domain", "operands doesn't belong to the domain of operator"}, {cat_truncation, "truncation", "data can be lost as a result of type conversion"}, {cat_bounds, "bounds", "array index or length is out of range"}, {cat_done, "done", "notification about verification completion"}, {cat_all, "all", "produce messages of all categories"} }; void usage() { fprintf(stderr, "\ Usage: jlint {option|java_class_file}\n\ Options:\n\ -help : print this text\n\ -source : path to directory with .java files\n\ -history : use history file to avoid repeated messages\n\ -max_shown_paths : max. shown paths between two sync. methods\n\ (+|-)verbose : output more/less information about program activity\n\ (+|-)message_category : select categories of messages to be reported\n\ (+|-)message_code : enable/disable concrete message\n\ Message categories:\n"); message_category group = msg_category_option[0].msg_cat; for (unsigned i = 0; i < items(msg_category_option); i++) { message_category msg_cat = msg_category_option[i].msg_cat; if ((msg_cat & ~group) != 0) { group = msg_cat; fprintf(stderr, "\n"); } fprintf(stderr, " %s : %s\n", msg_category_option[i].cat_name, msg_category_option[i].cat_desc); } if (verbose) { fprintf(stderr, "\nMessages: (category:code: \"text\")\n"); for (int i = 0; i < msg_last; i++) { for (unsigned j = 0; j < items(msg_category_option); j++) { if (msg_table[i].category == msg_category_option[j].msg_cat) { fprintf(stderr, " %s:%s: \"%s\"\n", msg_category_option[j].cat_name, msg_table[i].name, msg_table[i].format); break; } } } } } int main(int argc, char* argv[]) { int i, j; if (argc == 1) { usage(); return EXIT_FAILURE; } for (i = 1; i < argc; i++) { if (*argv[i] == '-' || *argv[i] == '+') { bool enabled = *argv[i] == '+'; char* option = &argv[i][1]; int n_cat = items(msg_category_option); msg_select_category_option* msg = msg_category_option; if (stricmp(option, "source") == 0 && i+1 < argc) { source_file_path = argv[++i]; source_file_path_len = strlen(source_file_path); if (source_file_path[source_file_path_len] == FILE_SEP) { source_file_path_len -= 1; } source_path_redefined = true; continue; } if (stricmp(option, "history") == 0 && i+1 < argc) { history = fopen(argv[++i], "a+"); if (history != NULL) { char his_buf[MAX_MSG_LENGTH]; fseek(history, 0, SEEK_SET); while (fgets(his_buf, sizeof his_buf, history)) { int len = strlen(his_buf); if (len > 0) { his_buf[len-1] = '\0'; message_node::add_to_hash(his_buf); } } fseek(history, 0, SEEK_END); } else { fprintf(stderr, "Failed to open history file '%s'\n", argv[i]); } continue; } if (stricmp(option, "max_shown_paths") == 0 && i+1 < argc) { if (sscanf(argv[++i], "%d", &max_shown_paths) != 1) { fprintf(stderr, "Bad parameter value for -max_shown_paths " "option: '%s'\n", argv[i]); } continue; } if (stricmp(option, "verbose") == 0) { verbose = enabled; if (verbose) { fprintf(stderr, "Jlint - program correctness verifier for Java" " version %3.2f ("__DATE__")\n", VERSION); } continue; } if (stricmp(option, "help") == 0) { usage(); return EXIT_SUCCESS; } for (j = 0; j < n_cat; j++) { if (stricmp(msg[j].cat_name, option) == 0) { if (enabled) { reported_message_mask |= msg[j].msg_cat; } else { reported_message_mask &= ~msg[j].msg_cat; } break; } } if (j == n_cat) { for (j = 0; j < msg_last; j++) { if (stricmp(msg_table[j].name, option) == 0) { msg_table[j].enabled = enabled; break; } } if (j == msg_last) { fprintf(stderr, "Unrecognized option %s\n", argv[i]); usage(); } return EXIT_FAILURE; } } else { char* file_name = argv[i]; if (!source_path_redefined) { source_file_path = file_name; char* dirend = strrchr(source_file_path, FILE_SEP); source_file_path_len = (dirend != NULL) ? dirend - source_file_path : 0; } proceed_file(file_name); } } class_desc::global_analysis(); message_at(msg_done, utf_string(""), 0, (void*)n_messages); return EXIT_SUCCESS; } jlint-3.0/jlint.d0000644000175000017500000001223107234134153014520 0ustar davehodaveho00000000000000// renamed swap to avoid conflict with STL swap JAVA_INSN(0, nop, 1) JAVA_INSN(1, aconst_null, 1) JAVA_INSN(2, iconst_m1, 1) JAVA_INSN(3, iconst_0, 1) JAVA_INSN(4, iconst_1, 1) JAVA_INSN(5, iconst_2, 1) JAVA_INSN(6, iconst_3, 1) JAVA_INSN(7, iconst_4, 1) JAVA_INSN(8, iconst_5, 1) JAVA_INSN(9, lconst_0, 1) JAVA_INSN(10, lconst_1, 1) JAVA_INSN(11, fconst_0, 1) JAVA_INSN(12, fconst_1, 1) JAVA_INSN(13, fconst_2, 1) JAVA_INSN(14, dconst_0, 1) JAVA_INSN(15, dconst_1, 1) JAVA_INSN(16, bipush, 2) JAVA_INSN(17, sipush, 3) JAVA_INSN(18, ldc, 2) JAVA_INSN(19, ldc_w, 3) JAVA_INSN(20, ldc2_w, 3) JAVA_INSN(21, iload, 2) JAVA_INSN(22, lload, 2) JAVA_INSN(23, fload, 2) JAVA_INSN(24, dload, 2) JAVA_INSN(25, aload, 2) JAVA_INSN(26, iload_0, 1) JAVA_INSN(27, iload_1, 1) JAVA_INSN(28, iload_2, 1) JAVA_INSN(29, iload_3, 1) JAVA_INSN(30, lload_0, 1) JAVA_INSN(31, lload_1, 1) JAVA_INSN(32, lload_2, 1) JAVA_INSN(33, lload_3, 1) JAVA_INSN(34, fload_0, 1) JAVA_INSN(35, fload_1, 1) JAVA_INSN(36, fload_2, 1) JAVA_INSN(37, fload_3, 1) JAVA_INSN(38, dload_0, 1) JAVA_INSN(39, dload_1, 1) JAVA_INSN(40, dload_2, 1) JAVA_INSN(41, dload_3, 1) JAVA_INSN(42, aload_0, 1) JAVA_INSN(43, aload_1, 1) JAVA_INSN(44, aload_2, 1) JAVA_INSN(45, aload_3, 1) JAVA_INSN(46, iaload, 1) JAVA_INSN(47, laload, 1) JAVA_INSN(48, faload, 1) JAVA_INSN(49, daload, 1) JAVA_INSN(50, aaload, 1) JAVA_INSN(51, baload, 1) JAVA_INSN(52, caload, 1) JAVA_INSN(53, saload, 1) JAVA_INSN(54, istore, 2) JAVA_INSN(55, lstore, 2) JAVA_INSN(56, fstore, 2) JAVA_INSN(57, dstore, 2) JAVA_INSN(58, astore, 2) JAVA_INSN(59, istore_0, 1) JAVA_INSN(60, istore_1, 1) JAVA_INSN(61, istore_2, 1) JAVA_INSN(62, istore_3, 1) JAVA_INSN(63, lstore_0, 1) JAVA_INSN(64, lstore_1, 1) JAVA_INSN(65, lstore_2, 1) JAVA_INSN(66, lstore_3, 1) JAVA_INSN(67, fstore_0, 1) JAVA_INSN(68, fstore_1, 1) JAVA_INSN(69, fstore_2, 1) JAVA_INSN(70, fstore_3, 1) JAVA_INSN(71, dstore_0, 1) JAVA_INSN(72, dstore_1, 1) JAVA_INSN(73, dstore_2, 1) JAVA_INSN(74, dstore_3, 1) JAVA_INSN(75, astore_0, 1) JAVA_INSN(76, astore_1, 1) JAVA_INSN(77, astore_2, 1) JAVA_INSN(78, astore_3, 1) JAVA_INSN(79, iastore, 1) JAVA_INSN(80, lastore, 1) JAVA_INSN(81, fastore, 1) JAVA_INSN(82, dastore, 1) JAVA_INSN(83, aastore, 1) JAVA_INSN(84, bastore, 1) JAVA_INSN(85, castore, 1) JAVA_INSN(86, sastore, 1) JAVA_INSN(87, pop, 1) JAVA_INSN(88, pop2, 1) JAVA_INSN(89, dup_x0, 1) JAVA_INSN(90, dup_x1, 1) JAVA_INSN(91, dup_x2, 1) JAVA_INSN(92, dup2_x0, 1) JAVA_INSN(93, dup2_x1, 1) JAVA_INSN(94, dup2_x2, 1) JAVA_INSN(95, Jswap, 1) JAVA_INSN(96, iadd, 1) JAVA_INSN(97, ladd, 1) JAVA_INSN(98, fadd, 1) JAVA_INSN(99, dadd, 1) JAVA_INSN(100, isub, 1) JAVA_INSN(101, lsub, 1) JAVA_INSN(102, fsub, 1) JAVA_INSN(103, dsub, 1) JAVA_INSN(104, imul, 1) JAVA_INSN(105, lmul, 1) JAVA_INSN(106, fmul, 1) JAVA_INSN(107, dmul, 1) JAVA_INSN(108, idiv, 1) JAVA_INSN(109, lidiv, 1) JAVA_INSN(110, fdiv, 1) JAVA_INSN(111, ddiv, 1) JAVA_INSN(112, irem, 1) JAVA_INSN(113, lrem, 1) JAVA_INSN(114, frem, 1) JAVA_INSN(115, drem, 1) JAVA_INSN(116, ineg, 1) JAVA_INSN(117, lneg, 1) JAVA_INSN(118, fneg, 1) JAVA_INSN(119, dneg, 1) JAVA_INSN(120, ishl, 1) JAVA_INSN(121, lshl, 1) JAVA_INSN(122, ishr, 1) JAVA_INSN(123, lshr, 1) JAVA_INSN(124, iushr, 1) JAVA_INSN(125, lushr, 1) JAVA_INSN(126, iand, 1) JAVA_INSN(127, land, 1) JAVA_INSN(128, ior, 1) JAVA_INSN(129, lor, 1) JAVA_INSN(130, ixor, 1) JAVA_INSN(131, lxor, 1) JAVA_INSN(132, iinc, 3) JAVA_INSN(133, i2l, 1) JAVA_INSN(134, i2f, 1) JAVA_INSN(135, i2d, 1) JAVA_INSN(136, l2i, 1) JAVA_INSN(137, l2f, 1) JAVA_INSN(138, l2d, 1) JAVA_INSN(139, f2i, 1) JAVA_INSN(140, f2l, 1) JAVA_INSN(141, f2d, 1) JAVA_INSN(142, d2i, 1) JAVA_INSN(143, d2l, 1) JAVA_INSN(144, d2f, 1) JAVA_INSN(145, i2b, 1) JAVA_INSN(146, i2c, 1) JAVA_INSN(147, i2s, 1) JAVA_INSN(148, lcmp, 1) JAVA_INSN(149, fcmpl, 1) JAVA_INSN(150, fcmpg, 1) JAVA_INSN(151, dcmpl, 1) JAVA_INSN(152, dcmpg, 1) JAVA_INSN(153, ifeq, 3) JAVA_INSN(154, ifne, 3) JAVA_INSN(155, iflt, 3) JAVA_INSN(156, ifge, 3) JAVA_INSN(157, ifgt, 3) JAVA_INSN(158, ifle, 3) JAVA_INSN(159, if_icmpeq, 3) JAVA_INSN(160, if_icmpne, 3) JAVA_INSN(161, if_icmplt, 3) JAVA_INSN(162, if_icmpge, 3) JAVA_INSN(163, if_icmpgt, 3) JAVA_INSN(164, if_icmple, 3) JAVA_INSN(165, if_acmpeq, 3) JAVA_INSN(166, if_acmpne, 3) JAVA_INSN(167, goto_near, 3) JAVA_INSN(168, jsr, 3) JAVA_INSN(169, ret, 2) JAVA_INSN(170, tableswitch, 15) JAVA_INSN(171, lookupswitch, 11) JAVA_INSN(172, ireturn, 1) JAVA_INSN(173, lreturn, 1) JAVA_INSN(174, freturn, 1) JAVA_INSN(175, dreturn, 1) JAVA_INSN(176, areturn, 1) JAVA_INSN(177, vreturn, 1) JAVA_INSN(178, getstatic, 3) JAVA_INSN(179, putstatic, 3) JAVA_INSN(180, getfield, 3) JAVA_INSN(181, putfield, 3) JAVA_INSN(182, invokevirtual, 3) JAVA_INSN(183, invokespecial, 3) JAVA_INSN(184, invokestatic, 3) JAVA_INSN(185, invokeinterface, 5) JAVA_INSN(186, unused, 0) JAVA_INSN(187, anew, 3) JAVA_INSN(188, newarray, 2) JAVA_INSN(189, anewarray, 3) JAVA_INSN(190, arraylength, 1) JAVA_INSN(191, athrow, 1) JAVA_INSN(192, checkcast, 3) JAVA_INSN(193, instanceof, 3) JAVA_INSN(194, monitorenter, 1) JAVA_INSN(195, monitorexit, 1) JAVA_INSN(196, wide, 10) JAVA_INSN(197, multianewarray, 4) JAVA_INSN(198, ifnull, 3) JAVA_INSN(199, ifnonnull, 3) JAVA_INSN(200, goto_w, 5) JAVA_INSN(201, jsr_w, 5) #undef JAVA_INSN jlint-3.0/jlint.hh0000644000175000017500000001004510001266370014666 0ustar davehodaveho00000000000000//-< JLINT.H >-------------------------------------------------------+--------+ // Jlint Version 3.0 (c) 1998 GARRET | ? | // (Java Lint) | /\| | // | / \ | // Created: 28-Mar-98 K.A. Knizhnik | / [] \ | // Version 2.X: Last update: 08-Aug-01 Cyrille Artho | GARRET | // Version 3.X: Last update: 20-Jul-03 Raphael Ackermann | | //-------------------------------------------------------------------+--------+ // Java verifier //-------------------------------------------------------------------+--------+ #ifndef __JLINT_HH__ #define __JLINT_HH__ #define VERSION 3.0 #include "types.hh" #include "message_node.hh" #include "utf_string.hh" #include "method_desc.hh" #include "field_desc.hh" #include "class_desc.hh" #include "constant.hh" #include "callee_desc.hh" #include "access_desc.hh" #include "graph.hh" #include "component_desc.hh" #include "var_desc.hh" #include "local_context.hh" #include "overridden_method.hh" #include "string_pool.hh" enum const_types { c_none, c_utf8, c_reserver, c_integer, c_float, c_long, c_double, c_class, c_string, c_field_ref, c_method_ref, c_interface_method_ref, c_name_and_type }; // // Constants for extracting zip file // #define LOCAL_HDR_SIG "\113\003\004" /* bytes, sans "P" (so unzip */ //--- ZIP_local_file_header layout --------------------------------------------- # define LREC_SIZE 26 /* lengths of local file headers */ # define L_VERSION_NEEDED_TO_EXTRACT_0 0 # define L_VERSION_NEEDED_TO_EXTRACT_1 1 # define L_GENERAL_PURPOSE_BIT_FLAG 2 # define L_COMPRESSION_METHOD 4 # define L_LAST_MOD_FILE_TIME 6 # define L_LAST_MOD_FILE_DATE 8 # define L_CRC32 10 # define L_COMPRESSED_SIZE 14 # define L_UNCOMPRESSED_SIZE 18 # define L_FILENAME_LENGTH 22 //used # define L_EXTRA_FIELD_LENGTH 24 //used //--- ZIP_central_directory_file_header layout --------------------------------- # define CREC_SIZE 42 /* length of directory headers */ # define C_VERSION_MADE_BY_0 0 # define C_VERSION_MADE_BY_1 1 # define C_VERSION_NEEDED_TO_EXTRACT_0 2 # define C_VERSION_NEEDED_TO_EXTRACT_1 3 # define C_GENERAL_PURPOSE_BIT_FLAG 4 # define C_COMPRESSION_METHOD 6 //used new # define C_LAST_MOD_FILE_TIME 8 # define C_LAST_MOD_FILE_DATE 10 # define C_CRC32 12 # define C_COMPRESSED_SIZE 16 //used # define C_UNCOMPRESSED_SIZE 20 // used new # define C_FILENAME_LENGTH 24 //used # define C_EXTRA_FIELD_LENGTH 26 //used # define C_FILE_COMMENT_LENGTH 28 # define C_DISK_NUMBER_START 30 # define C_INTERNAL_FILE_ATTRIBUTES 32 # define C_EXTERNAL_FILE_ATTRIBUTES 34 # define C_RELATIVE_OFFSET_LOCAL_HEADER 38 //used //--- ZIP_end_central_dir_record layout ---------------------------------------- # define ECREC_SIZE 18 /* length of central dir record */ /* define SIGNATURE 0 space-holder only */ # define NUMBER_THIS_DISK 4 # define NUM_DISK_WITH_START_CENTRAL_DIR 6 # define NUM_ENTRIES_CENTRL_DIR_THS_DISK 8 # define TOTAL_ENTRIES_CENTRAL_DIR 10 //used # define SIZE_CENTRAL_DIRECTORY 12 //used # define OFFSET_START_CENTRAL_DIRECTORY 16 # define ZIPFILE_COMMENT_LENGTH 20 // -- possible values at place C_COMPRESSION_METHOD # define C_DEFLATE 8 # define C_UNCOMPRESSED 0 #endif jlint-3.0/jlint.msg0000644000175000017500000001040507244526454015076 0ustar davehodaveho00000000000000// //MSG(category, code, pos, text) // // Macro parameter "pos" is 1 if message is position dependent and it's // location should be included in history, otherwise if 0 - message is position // independent and fully qualified by code and parameters. // // Parameters %0s and %1d are used for file name and line number within file, // so all parameters used in messages should start from %2. // Possible formats are 'd' for integer, 's' for zero terminated strings, // 'm' for methods, 'c' for classes, 'u' for utf8 strings. // // // Synchronization // MSG(deadlock,sync_loop,0,"Loop %2d: invocation of synchronized method %3m can cause deadlock") MSG(deadlock,loop,0,"Loop %2d/%3d: invocation of method %4m forms the loop in class dependency graph") MSG(deadlock,lock,0,"Lock %2s is requested while holding lock %3s, with other thread holding %2s and requesting lock %3s") MSG(deadlock,wait,0,"Method wait() can be invoked with monitor of other object locked") MSG(deadlock,locklist,0,"Holding %2d lock(s):%3s") MSG(deadlock,wait_path,0,"Call sequence to method %2m can cause deadlock in wait()") MSG(race_condition,nosync,0,"Synchronized method %2m is overridden by non-synchronized method of derived class '%3c'") MSG(race_condition,concurrent_call,0,"Method %2m can be called from different threads and is not synchronized") MSG(race_condition,concurrent_access,0,"Field '%2u' of class '%3c' can be accessed from different threads and is not volatile") MSG(race_condition,run_nosync,0,"Method %2m implementing 'Runnable' interface is not synchronized") MSG(race_condition,lock_assign,0,"Value of lock %2u is changed outside synchronization or constructor") MSG(race_condition,lock_assign2,0,"Value of lock %2u is changed while (potentially) owning it") // MSG(wait_nosync,wait_nosync,0,"Method %2u is called from non-synchronized method") MSG(wait_nosync,wait_nosync,0,"Method '%2s.%3u' is called without synchronizing on '%2s'") // // Inheritance // MSG(not_overridden,not_overridden,0,"Method %2m is not overridden by method with the same name of derived class '%3c'") MSG(not_overridden,equals_not_overridden,0,"hashCode() was overridden but not equals()") MSG(not_overridden,hashcode_not_overridden,0,"equals() was overridden but not hashCode()") MSG(field_redefined,field_redefined,0,"Component '%2u' in class '%3c' shadows one in base class '%4c'") MSG(shadow_local,shadow_local,1,"Local variable '%2u' shadows component of class '%3c'") MSG(super_finalize,super_finalize,0,"Method finalize() doesn't call super.finalize()") // // Data flow // MSG(null_reference,null_param,0,"Method %2m can be invoked with NULL as %3d parameter and this parameter is used without check for NULL") MSG(null_reference,null_var,1,"Value of referenced variable '%2u' may be NULL") MSG(null_reference,null_ptr,1,"NULL reference can be used") MSG(zero_operand,zero_operand,1,"Zero operand for %2s operation") MSG(zero_result,zero_result,1,"Result of operation %2s is always 0") MSG(domain,shift_count,1,"Shift %2s with count %3s than %4d") MSG(domain,shift_range,1,"Shift %2s count range [%3d,%4d] is out of domain") MSG(domain,conversion,1,"Range of expression value has no intersection with %2s type domain") MSG(truncation,truncation,1,"Data can be lost as a result of truncation to %2s") MSG(overflow,overflow,1,"Maybe type cast is not correctly applied") MSG(redundant,same_result,1,"Comparison always produces the same result") MSG(redundant,disjoint_mask,1,"Compared expressions can be equal only when both of them are 0") MSG(redundant,no_effect,1,"Reminder always equal to the first operand") MSG(short_char_cmp,short_char_cmp,1,"Comparison of short with char") MSG(string_cmp,string_cmp,1,"Compare strings as object references") MSG(weak_cmp,weak_cmp,1,"Inequality comparison can be replaced with equality comparison") MSG(incomp_case, incomp_case,1,"Switch case constant %2d can't be produced by switch expression") MSG(bounds, neg_len,1,"Array length [%2d,%3d] is less than zero") MSG(bounds, maybe_neg_len,1,"Array length [%2d,%3d] may be less than zero") MSG(bounds, bad_index,1,"Index [%2d,%3d] is out of array bounds") MSG(bounds, maybe_bad_index,1,"Index [%2d,%3d] may be out of array bounds") // Special case: no location prefix is appended to this message MSG(done, done, 0, "Verification completed: %0d reported messages") #undef MSG jlint-3.0/jlint.sh0000755000175000017500000000037307653162217014725 0ustar davehodaveho00000000000000#!/bin/bash find . -name '*.class' | xargs jlint -not_overridden \ -redundant -weak_cmp -bounds -zero_operand -string_cmp -shadow_local | \ grep -v 'Field .class\$' | grep -v 'can be .*ed from different threads' | \ grep -v 'Method.*Runnable.*synch' jlint-3.0/jlint_3.0.tex0000644000175000017500000005205710001256121015451 0ustar davehodaveho00000000000000\documentclass[11pt,twoside,a4paper,draft]{article} \usepackage{verbatim} \author{Raphael Ackermann raphy@student.ethz.ch} \title{Jlint -- status of version 3.0} \begin{document} \maketitle \newpage \tableofcontents \newpage \section {Introduction} \texttt{jlint} is a static model checker for java programs. \newline It can be found at the following URL's: http://www.artho.com/jlint/ \newline http://www.sf.org/jlint/ \newline The development activity of \texttt{jlint} has been very low in the last two years. At the same time a new version of the gnu compiler g++ 3.X and a new java version 1.4 became quite popular. Java 1.4 introduced some changes in the way java programs were compiled. These changes caused a crash in \texttt{jlint} when analysing certain class files. Furthermore, jlint's sources couldn't be be compiled using the new g++ 3.x compiler and therefore in a first step the sources had to be changed to allow compilation using a new gnu compiler. In a second step the bug was to be fixed. There were also a couple of other things which suggested to revise jlint and to do a new major release with an up to date jlint. This meant writing the hitherto missing ./configure script, merging some patches, making jlint able to work on 64 bit architectures, and eliminating the warnings of valgrind. Such a long time of almost no development is of course not wanted and a broader basis and more active development on jlint was looked for. It is hoped that this goal will be reached by moving the CVS repositories and the web page to www.sourceforge.net where everybody can contribute to the further development of jlint. While fixing the bug, a test framework was developed which supports regression testing on new versions and different platforms. Unit Testing could be added in a future release. % ########################################################################## \section {Test Framework} % ########################################################################## A test framework allows you to control the changes made in the source code during development. Such a framework can consist of several kind of tests. The one test pattern we will look at here is regression tests. In a regression test you need to have a predefined behaviour and output of your program before actually running any tests. The goal is then that on every platform and for every future version of the program the output matches the specified output and that the program works in a predefined manner. Testing on different platforms is important because e.g., Macintosh computers use a different memory alignment than windows IA32 architectures. Running the test framework after every change in the source code and checking if the output still matches the original output is a good way to find an error or to find out whether a change in the sources which seems to be correct has any side effects on another part of the program. There are different kind of regression tests possible. Two of them are black box testing and white box testing. Black box testing as it was added to \texttt{jlint} in this semester project works roughly like this: You need a number of different inputs, the test cases. A correct version of the program is then used to create the predefined outputs and indicated above. The inputs should never change, once they do, you will have to create new outputs. The created output files are saved and are referred to as reference outputs. They specify the desired output which should be matched by future versions of your program. \begin{verbatim} create test framework run test framework input input | | V V |---------| |--------| | | | | | black | | black | | box | | box | | | | | |---------| |--------| | | V V reference output output who should match the reference output \end{verbatim} Imagine you change some part of your program and want to know if it still works correctly. Just run your test framework with the input files and compare your output with the reference output. In case the two outputs are not identical, you will have to analyse the changes made in your program. There are two possibilities now. Either your changes have undesired side effects and you have to implement the changes in some other way. This is the more common case. Or you have changed something in the logic of your program and you know that your new input is valid and correct. Then you have to change your test framework to include the new output as the new reference output. Black box means that the test result do not tell anything about how jlint calculates its resulting output or whether the algorithms used are programmed correctly. To do this, one would use unit testing. Unit testing is a kind of white box testing where you look at the internals of the box. You try to test single components for a specified behaviour. Unit testing could be implemented in a future version of the test framework. A small test framework was added to \texttt{jlint} to allow testing the behaviour of the program. The test framework consists of a sample of class files together with the output that \texttt{jlint} generates when run on these files. The sample class files are chosen to cover most of \texttt{jlint's} possible outcomes. To produce this output \texttt{jlint} version 3.0 was used and the output generated was stored in *.out files. These *.out files represent the reference output which should be matched by all future versions of \texttt{jlint} unless one changes something in the logic of \texttt{jlint}. Whenever there will be changes in the source code of \texttt{jlint}, the test framework should be used to make sure that the output of the new version is the same as the reference output. To run the test framework you need to go into the test/ directory, where you execute \texttt{./testall.sh}. \\Script \texttt{testall.sh} itself calls \texttt{runtest.sh}, \texttt{show\-diff.sh} and \texttt{showerror.sh}. \\\\Script \texttt{runtest.sh} runs jlint on the sample java class files and writes its output into the *.log and its error messages into *.err. \\\\Script \texttt{showdiff.sh} compares the *.out generated by \texttt{runtest.sh} with the stored *.log files and in case of a difference makes a diff x.{out,log} \\\\Script \texttt{showerror.sh} looks for *.err file with size bigger than 0 and produces a warning and the name of the file if this happens. should not produce any output if nothing goes wrong. Only in the case that \texttt{jlint} crashes, it reports which files caused \texttt{jlint} to abort. \begin{verbatim} Test Framework (added in Version 3.0) ===================================== File locations -------------- class files to be tested tests/test#/ log and reference output files tests/log/ Tests: ------ 4 tests as of Version 3.0 test1 test.java test2 class showing finally bug test3 sample from java/io/* (1.3) test4 sample from java/io/* (1.4) in case of error: # default run run #test 1 log/1.out 1.log 1.err #test 2 log/2.out 2.log 2.err #test 3 log/3.out 3.log 3.err #test 4 log/4.out 4.log 4.err ... Usage: ------ To run test suite using valgrind, try "./testall.sh --valgrind" For info on how to run tests, try "./testall.sh --help" \end{verbatim} % ########################################################################## \section {Adding New Test Cases} % ########################################################################## Adding new test cases to the test framework is quite simple. A new directory e.g., test5 has to be created and the class files to be tested must be copied into that directory. In file \texttt{testall.sh} the variable \texttt{NROFTESTS} which is currently set to 4 has to be changed to reflect the new number of test cases. First of all a new reference file e.g., 5.out has to be created in the directory \texttt{tests/log/}. One has to be careful to use a fully functional (bug-free) version of \texttt{jlint} to produce a new reference *.out file. Once this is done \texttt{testall.sh} can be called and will run the tests including the newly added ones. % ########################################################################## \section {Found Errors, Bug Fixes} % ########################################################################## % ########################################################################## \subsection {try--catch--finally constructs in Java 1.3 and Java 1.4} % ########################################################################## Before \texttt{java 1.4}, byte code verifiers of the java virtual machine had difficulties verifying the correctness of exception handlers with a complex control flow. These complex exception handlers were the result of a try--finally or a try--catch--finally construct. Starting with \texttt{java 1.4} this bug in the virtual machine was worked around by changing the compiler. The generated byte code remains the same, but the number and the range of the exception handlers was changed in case there is a finally statement in the code. Below you can see a java source file, followed by the corresponding byte code and the exception table. Differences between \texttt{java 1.3} and \texttt{java 1.4} will be pointed out. \begin{verbatim} class SC { void m(boolean b) { try { if (b) return; } finally { b = false; } } } \end{verbatim} Both java compilers produce exactly the same byte code in this case. \\lines 0 to 7 handle the case that b == true \\lines 8 to 11 handle the case that b == false \\lines 14 to 19 handle the case that an exception occurs during the try block. \\lines 20 to 23 are for the \texttt{finally} statement which has to be executed in any case. \begin{verbatim} void m(boolean arg1) Code(max_stack = 1, max_locals = 4, code_length = 26) 0: iload_1 //put b on top of stack 1: ifeq #8 //if b != 0 4: jsr #20 //execute finally block 7: return //exit 8: jsr #20 //execute finally block 11: goto #25 //goto exit statement 14: astore_2 //store exception 15: jsr #20 //execute finally block 18: aload_2 //load exception 19: athrow //rethrow exception because //no catch statement 20: astore_3 21: iconst_0 //put 0 (false) on stack 22: istore_1 //b = false 23: ret %3 //return to stmt after jsr 25: return //exit \end{verbatim} In this code, compiled by \texttt{javac 1.3}, there is only one exception handler which is valid for lines 0 to 14 exclusive. This means it catches exceptions occurring on lines 0 to and including 11. Whereas in the exception table below, compiled by \texttt{javac 1.4} there is also just one exception handler. But the range is split twice. Lines 7 and 11, the ``return'' and the ``goto'' instruction are excluded from the range. Instructions such as ``return'' and ``goto'' can be excluded from the range because they can never throw an exception. \begin{verbatim} Exception handler(s) = >From To Handler Type 0 14 14 (0) Exception handler(s) = >From To Handler Type 0 7 14 (0) 8 11 14 (0) 14 18 14 (0) \end{verbatim} In the next section it is shown how jlint 2.3 failed to correctly interpret this new kind of exception table and how this bug was fixed in jlint version 3.0. % ########################################################################## \subsection {Context Handling in \texttt{Jlint}} % ########################################################################## In its analysis of the class file jlint goes through the byte code calculating the range of possible values for each variable. For this a context data structure is used. In this context data structure the range of values a variable can have is saved. Contexts can be created, split and merged. Every byte code address has a linked list of contexts. In the code fragment below which is from file \texttt{jlint.cc} for every entry in the exception table, such a context is inserted into the linked list at position ``handler\_pc'' by calling ``ctx\_entry\_point(\&method-\verb+>+context[handler\_pc]);''. \begin{verbatim} while (--exception_table_length >= 0) { int handler_pc = unpack2(fp+4); new ctx_entry_point(&method->context[handler_pc]); fp += 8; } \end{verbatim} After that the byte code instructions are analysed in jlint. Whenever \texttt{Jlint} starts the analysis of a new instruction, it goes through the linked list which corresponds to the instruction being analysed. The stack pointer is increased by one for each of the contexts found in the list. If for example there is an exception handler at position 14 which handles Exception $n > 1$. Then the linked list of position 14 has $n$ entry point contexts amongst possibly other contexts. And so the stack pointer has been increased by $n$ instead of only one. The problem here is that the stack pointer will only be reduced by one because there is only one exception handler in the byte code at a specific position, even if there is more than one range for the same exception handler. And so there is only one \texttt{astore} instruction where jlint will decrease the stack pointer by one. Therefore after adding more than one exception context at the same byte code address the final assertion sp == stack\_bottom fails and jlint returns with an error. \begin{verbatim} ------| |-------| e3 |astore1| |aload1 |astore1| ------|-------|-------|-------|-------|-------| e2 | | | | | | ------|-------|-------|-------|-------|-------| e1 | | | | | end | ------|-------|-------|-------|-------|-------| stack_bottom t1 t2 t3 t4 t5 t6 \end{verbatim} At time t2 the exception is stored and the stack pointer is decreased by one. But the stack pointer is still 2 positions too high and it will remain too high until the end, where the assertion assert(sp == stack\_bottom) will fail. % ########################################################################## \subsection {Bug Related Errors} % ########################################################################## This bug caused jlint to exit abnormally under certain circumstances. Two different assertions could be violated because the simulated stack management didn't work properly any more. The First kind of error occurred e.g., when analysing file \newline \texttt{/java/io/BufferedReader.class}. \begin{verbatim} in file: method_desc.cc in method: parse_code(constant **, const field_desc *) Assertion `sp == stack_bottom' failed. Aborted \end{verbatim} The Second kind of error occurred e.g., when analysing file \newline \texttt{/java/lang/FloatingDecimal.class}. \begin{verbatim} in file: local_context.cc in method: transfer(...): Assertion `sp == come_from->stack_pointer' failed. Aborted \end{verbatim} % ########################################################################## \subsection {Minimal class file that reproduced the bug} % ########################################################################## Below is the listing of a minimal java class file which caused jlint to abort. This class file does not have a meaning, but still it is valid and gives a class file with a minimal number of byte code instructions. \begin{verbatim} class SC { void m() { try { } finally { } } } \end{verbatim} % ########################################################################## \subsection {Bug fix} % ########################################################################## Here is the diff of the old and the new version of \texttt{jlint.cc}: \begin{verbatim} diff -u jlint_old.cc jlint.cc --- jlint_old.cc 2003-04-26 15:29:23.000000000 +0200 +++ jlint.cc 2003-08-23 15:23:36.000000000 +0200 @@ -504,10 +504,45 @@ sizeof(local_context*)*(code_length+1)); int exception_table_length = unpack2(fp); fp += 2; + + /* add new entry for each distinct "byte code address + ** of handle". + ** + ** if an exception handler at byte code "pos" handles + ** exception of more than one byte code range, call + ** "new ctx_entry_point(&method->context[pos]);" only + ** once! Because otherwise the stack gets out of + ** control. + ** + ** in the following example there are two different + ** handle addresses 16 and 25. and for each of them + **"new ctx_entry_point(&method->context[handler_pc]);" + ** is called exactly once. Therefore the program calls + ** new ctx_entry_point(&method->context[16]); + ** new ctx_entry_point(&method->context[25]); + ****************************************************** + ** Example Exception Table: ** + ** ------------------------------------- ** + ** ** + ** byte code address ** + ** from to of handle ** + ** 2 10 16 ** + ** 12 14 16 ** + ** 20 23 25 ** + ****************************************************** + ** + ** it is expected that the byte code addresses of the + ** handles are ordered. If this would not be the case, + ** a simple comparison of handler_pc and + ** old_handler_pc would not be sufficient! + */ + + int old_handler_pc = -1; + while (--exception_table_length >= 0) { int handler_pc = unpack2(fp+4); - new ctx_entry_point(&method->context[handler_pc]); - fp += 8; + if ( handler_pc != old_handler_pc) { + new ctx_entry_point(&method->context[handler_pc]); + } + fp += 8; + old_handler_pc = handler_pc; } int method_attr_count = unpack2(fp); fp += 2; \end{verbatim} % ########################################################################## \section {Changes to the Build Process} % ########################################################################## A new and automated build process was being started to work on during this Semester project. Before, there was no ./configure. The \texttt{Makefile} had to be changed manually if one wanted to set some architecture specific flags or if one wanted a debugging build or a build for a different target machine. The dependencies for compilation are automatically generated using a perl script: \texttt{mkmf.pl}. A very basic configure script was added which calculates the options and sets environment variables depending on the Operating System. This configure script generates the \texttt{Makefile}. More precisely it takes a standard \texttt{Makefile.in} and replaces the unspecified options by values it calculates. The new target test\_dist was added to the \texttt{Makefile}. ''make test\_dist'' builds a tar.gz of the sources including the test directory with all the test files. It is intended to be used by future developers of \texttt{jlint} to be able to check for errors. % ########################################################################## \section {Results and Conclusion} % ########################################################################## What has been done and what still needs to be done: \\ The sources can be compiled on Intel IA32 Architecture using Linux as OS and GCC 3.X as compiler. The finally bug was fixed. The file \texttt{method\_desc.cc} got a better documentation. Two patches got merged. A basic configure script has been written (only works for linux on IA32 and not even here it is guaranteed to work). A test framework was added which should make it easier to check for errors in the future. The configure script should be improved and support for other architectures as well as other operating systems should be added. The cvs repository should be moved to the sourceforge account. Support for 64-bit architectures needs to be implemented as well. The valgrind tests in the test framework don't really work yet. The problem is how to run valgrind with shell scripts. Some more things which one could add to improve jlint and to fix some open bugs can be found in the files BUGS and TODO which come with \texttt{jlint}. \end{document} jlint-3.0/local_context.cc0000644000175000017500000005115107712370213016404 0ustar davehodaveho00000000000000#include "local_context.hh" /* ** every subclass of local_context has a transfer method which keeps track ** of the variable states during branching. These transfer methods are ** called in method_desc::parse_code( ** constant** constant_pool, const field_desc* is_this) ** for every bytecode and at the end after parsing all the bytecode of the method ** before cleanup to pop up all the local variables. */ vbm_operand* ctx_push_var::transfer(method_desc* method, vbm_operand* sp, byte, byte&) { var_desc* var = &method->vars[var_index]; if (var->type == tp_void) { if (IS_INT_TYPE(var_type)) { var->min = ranges[var_type].min; var->max = ranges[var_type].max; var->mask = var->min|var->max; } else if (var_type == tp_long) { var[0].min = 0x80000000; var[0].max = 0x7fffffff; var[0].mask = 0xffffffff; var[1].min = 0; var[1].max = 0xffffffff; var[1].mask = 0xffffffff; } else { var->mask = (var->type == tp_self) ? var_desc::vs_not_null : var_desc::vs_unknown; var->min = 0; var->max = MAX_ARRAY_LENGTH; } } var->type = var_type; var->name = *var_name; var->start_pc = var_start_pc; return sp; } vbm_operand* ctx_pop_var::transfer(method_desc* method, vbm_operand* sp, byte, byte&) { var_desc* var = &method->vars[var_index]; for (field_desc* field = method->cls->fields; field != NULL; field = field->next) { // Do not produce message about shadowing of class camponent by local // variable in case when local variable is formal parameter of the // method and programmer explicitly refer class object component // by "this": this.x = x; if (field->name == var->name && (!(field->attr & field_desc::f_used) || var->start_pc != 0 /* not formal parameter*/)) { method->message(msg_shadow_local, var->start_pc, &var->name, method->cls); break; } } var->type = tp_void; var->name = utf_string("???"); return sp; } vbm_operand* ctx_split::transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop) { if (n_branches > 0 && cmd == cmd_save_ctx) { vars = new var_desc[method->n_vars]; memcpy(vars, method->vars, method->n_vars*sizeof(var_desc)); } else { vars = NULL; } switch_var_index = -1; in_monitor = method->in_monitor; vbm_operand* left_op = sp-2; vbm_operand* right_op = sp-1; switch (cop) { case jsr: case jsr_w: sp->type = tp_object; sp->mask = var_desc::vs_not_null; sp->min = 0; sp->max = MAX_ARRAY_LENGTH; stack_pointer = sp+1; break; case tableswitch: case lookupswitch: stack_pointer = right_op; switch_var_index = right_op->index; break; case ifeq: if (vars != NULL && right_op->index >= 0) { // state at the branch address var_desc* var = &vars[right_op->index]; if (prev_cop == iand) { // Operation of form (x & const) == 0 if (IS_INT_TYPE(var->type)) { var->mask &= ~right_op->mask; } } else { var->max = var->min = 0; if (IS_INT_TYPE(var->type)) { var->mask = 0; } } } stack_pointer = right_op; break; case ifne: if (right_op->index >= 0) { // value of local var. was pushed on stack // state after if var_desc* var = &method->vars[right_op->index]; if (prev_cop == iand) { // Operation of form (x & const) != 0 if (IS_INT_TYPE(var->type)) { var->mask &= ~right_op->mask; } } else { var->max = var->min = 0; if (IS_INT_TYPE(var->type)) { var->mask = 0; } } } stack_pointer = right_op; break; case iflt: if (right_op->index >= 0) { // state after if var_desc* var = &method->vars[right_op->index]; if (var->min < 0) var->min = 0; if (var->max < 0) var->max = 0; if (IS_INT_TYPE(var->type)) { var->mask &= ~SIGN_BIT; } if (vars != NULL) { // forward branch // state at the branch address var = &vars[right_op->index]; if (var->min >= 0) var->min = -1; if (var->max >= 0) var->max = -1; } } stack_pointer = right_op; break; case ifge: if (right_op->index >= 0) { // state after if var_desc* var = &method->vars[right_op->index]; if (var->min >= 0) var->min = -1; if (var->max >= 0) var->max = -1; if (vars != NULL) { // forward branch // state at the branch address var = &vars[right_op->index]; if (var->min < 0) var->min = 0; if (var->max < 0) var->max = 0; if (IS_INT_TYPE(var->type)) { var->mask &= ~SIGN_BIT; } } } stack_pointer = right_op; break; case ifgt: if (right_op->index >= 0) { // state after if var_desc* var = &method->vars[right_op->index]; if (var->min > 0) var->min = 0; if (var->max > 0) var->max = 0; if (vars != NULL) { // forward branch // state at the branch address var = &vars[right_op->index]; if (var->min <= 0) var->min = 1; if (var->max <= 0) var->max = 1; if (IS_INT_TYPE(var->type)) { var->mask &= ~SIGN_BIT; } } } stack_pointer = right_op; break; case ifle: if (right_op->index >= 0) { // state after if var_desc* var = &method->vars[right_op->index]; if (var->min <= 0) var->min = 1; if (var->max <= 0) var->max = 1; if (IS_INT_TYPE(var->type)) { var->mask &= ~SIGN_BIT; } if (vars != NULL) { // forward branch // state at the branch address var = &vars[right_op->index]; if (var->min > 0) var->min = 0; if (var->max > 0) var->max = 0; } } stack_pointer = right_op; break; case if_icmpeq: if (vars != NULL) { // state at the branch address if (right_op->index >= 0) { var_desc* var = &vars[right_op->index]; if (var->min < left_op->min) var->min = left_op->min; if (var->max > left_op->max) var->max = left_op->max; if (var->min > var->max) var->min = var->max; // recovery if (IS_INT_TYPE(var->type)) { var->mask &= left_op->mask; } } if (left_op->index >= 0) { var_desc* var = &vars[left_op->index]; if (var->min < right_op->min) var->min = right_op->min; if (var->max > right_op->max) var->max = right_op->max; if (var->min > var->max) var->min = var->max; // recovery if (IS_INT_TYPE(var->type)) { var->mask &= right_op->mask; } } } stack_pointer = left_op; break; case if_icmpne: if (right_op->index >= 0) { // state after if var_desc* var = &method->vars[right_op->index]; if (var->min < left_op->min) var->min = left_op->min; if (var->max > left_op->max) var->max = left_op->max; if (var->min > var->max) var->min = var->max; // recovery if (IS_INT_TYPE(var->type)) { var->mask &= left_op->mask; } } if (left_op->index >= 0) { var_desc* var = &method->vars[left_op->index]; if (var->min < right_op->min) var->min = right_op->min; if (var->max > right_op->max) var->max = right_op->max; if (var->min > var->max) var->min = var->max; // recovery if (IS_INT_TYPE(var->type)) { var->mask &= right_op->mask; } } stack_pointer = left_op; break; case if_icmplt: if (right_op->index >= 0) { // left >= right var_desc* var = &method->vars[right_op->index]; if (var->max > left_op->max) { var->max = left_op->max; if (var->min > var->max) var->min = var->max; } if (vars != NULL) { // left < right var = &vars[right_op->index]; if (var->min <= left_op->min) { var->min = left_op->min == ranges[tp_int].max ? left_op->min : left_op->min+1; if (var->min > var->max) var->max = var->min; } } } if (left_op->index >= 0) { // left >= right var_desc* var = &method->vars[left_op->index]; if (var->min < right_op->min) { var->min = right_op->min; if (var->min > var->max) var->max = var->min; } if (vars != NULL) { // left < right var = &vars[left_op->index]; if (var->max >= right_op->max) { var->max = right_op->max == ranges[tp_int].min ? right_op->max : right_op->max-1; if (var->min > var->max) var->min = var->max; } } } stack_pointer = left_op; break; case if_icmple: if (right_op->index >= 0) { // left > right var_desc* var = &method->vars[right_op->index]; if (var->max >= left_op->max) { var->max = left_op->max == ranges[tp_int].min ? left_op->max : left_op->max-1; if (var->min > var->max) var->min = var->max; } if (vars != NULL) { // left <= right var = &vars[right_op->index]; if (var->min < left_op->min) { var->min = left_op->min; if (var->min > var->max) var->max = var->min; } } } if (left_op->index >= 0) { // left > right var_desc* var = &method->vars[left_op->index]; if (var->min <= right_op->min) { var->min = right_op->min == ranges[tp_int].max ? right_op->min : right_op->min+1; if (var->min > var->max) var->max = var->min; } if (vars != NULL) { // left <= right var = &vars[left_op->index]; if (var->max > right_op->max) { var->max = right_op->max; if (var->min > var->max) var->min = var->max; } } } stack_pointer = left_op; break; case if_icmpgt: if (right_op->index >= 0) { // left <= right var_desc* var = &method->vars[right_op->index]; if (var->min < left_op->min) { var->min = left_op->min; if (var->min > var->max) var->max = var->min; } if (vars != NULL) { // left > right var = &vars[right_op->index]; if (var->max >= left_op->max) { var->max = left_op->max == ranges[tp_int].min ? left_op->max : left_op->max-1; if (var->min > var->max) var->min = var->max; } } } if (left_op->index >= 0) { // left <= right var_desc* var = &method->vars[left_op->index]; if (var->max > right_op->max) { var->max = right_op->max; if (var->min > var->max) var->min = var->max; } if (vars != NULL) { // left > right var = &vars[left_op->index]; if (var->min <= right_op->min) { var->min = right_op->min == ranges[tp_int].max ? right_op->min : right_op->min+1; if (var->min > var->max) var->max = var->min; } } } stack_pointer = left_op; break; case if_icmpge: if (right_op->index >= 0) { // left < right var_desc* var = &method->vars[right_op->index]; if (var->min <= left_op->min) { var->min = left_op->min == ranges[tp_int].max ? left_op->min : left_op->min+1; if (var->min > var->max) var->max = var->min; } if (vars != NULL) { // left >= right var = &vars[right_op->index]; if (var->max > left_op->max) { var->max = left_op->max; if (var->min > var->max) var->min = var->max; } } } if (left_op->index >= 0) { // left < right var_desc* var = &method->vars[left_op->index]; if (var->max >= right_op->max) { var->max = right_op->max == ranges[tp_int].min ? right_op->max : right_op->max-1; if (var->min > var->max) var->min = var->max; } if (vars != NULL) { // left >= right var = &vars[left_op->index]; if (var->min < right_op->min) { var->min = right_op->min; if (var->min > var->max) var->max = var->min; } } } stack_pointer = left_op; break; case if_acmpeq: if (vars != NULL) { if (right_op->index >= 0) { vars[right_op->index].mask &= left_op->mask; } if (left_op->index >= 0) { vars[left_op->index].mask &= right_op->mask; } } stack_pointer = left_op; break; case if_acmpne: if (right_op->index >= 0) { method->vars[right_op->index].mask &= left_op->mask; } if (left_op->index >= 0) { method->vars[left_op->index].mask &= right_op->mask; } stack_pointer = left_op; break; case ifnull: if (right_op->index >= 0) { method->vars[right_op->index].mask |= var_desc::vs_not_null; if (vars != NULL) { vars[right_op->index].mask &= ~var_desc::vs_not_null; } } stack_pointer = right_op; break; case ifnonnull: if (right_op->index >= 0) { method->vars[right_op->index].mask &= ~var_desc::vs_not_null; if (vars != NULL) { vars[right_op->index].mask |= var_desc::vs_not_null; } } stack_pointer = right_op; break; default: stack_pointer = sp; } stack_top[1] = stack_pointer[-1]; stack_top[0] = stack_pointer[-2]; return sp; } /* ** merge states after split */ vbm_operand* ctx_merge::transfer(method_desc* method, vbm_operand* sp, byte, byte& prev_cop) { var_desc save_var; method->in_monitor = come_from->in_monitor; if (cmd == cmd_case_ctx && come_from->switch_var_index >= 0) { // If branch is part of switch and switch expression is local variable, // then we know value of this variable if this branch takes place var_desc* var = &come_from->vars[come_from->switch_var_index]; save_var = *var; var->max = var->min = var->mask = case_value; } var_desc* v0 = method->vars; var_desc* v1 = come_from->vars; if (prev_cop == goto_near || prev_cop == goto_w || prev_cop == ret || prev_cop == athrow || prev_cop == lookupswitch || prev_cop == tableswitch || unsigned(prev_cop - ireturn) <= unsigned(vreturn-ireturn)) { // Control can be passed to this point only by branch: // no need to merge states for (int i = method->n_vars; --i >= 0; v0++, v1++) { if (v0->type == v1->type) { v0->min = v1->min; v0->max = v1->max; v0->mask = v1->mask; } else if (v1->type == tp_void) { v0->type = tp_void; } } sp = come_from->stack_pointer; sp[-1] = come_from->stack_top[1]; sp[-2] = come_from->stack_top[0]; // all successive ctx_merge::transfer should merge variables properties prev_cop = nop; } else { // merge states for (int i = method->n_vars; --i >= 0; v0++, v1++) { if (v0->type == v1->type) { if (IS_INT_TYPE(v0->type)) { if (v0->min > v1->min) v0->min = v1->min; if (v0->max < v1->max) v0->max = v1->max; v0->mask |= v1->mask; #ifdef INT8_DEFINED } else if (v0->type == tp_long) { int8 min0 = LOAD_INT8(v0,min); int8 max0 = LOAD_INT8(v0,max); int8 mask0 = LOAD_INT8(v0,mask); int8 min1 = LOAD_INT8(v1,min); int8 max1 = LOAD_INT8(v1,max); int8 mask1 = LOAD_INT8(v1,mask); if (min0 > min1) { STORE_INT8(v0, min, min1); } if (max0 < max1) { STORE_INT8(v0, max, max1); } mask0 |= mask1; STORE_INT8(v0, mask, mask0); v0 += 1; v1 += 1; i -= 1; assert(i >= 0); #endif } else { if (v0->min > v1->min) v0->min = v1->min; if (v0->max < v1->max) v0->max = v1->max; v0->mask &= v1->mask; } } else if (v0->type != tp_void && v1->type == tp_void) { if (IS_INT_TYPE(v0->type)) { v0->min = ranges[tp_int].min; v0->max = ranges[tp_int].max; v0->mask = ALL_BITS; } else if (v0->type == tp_long) { v0[0].min = 0x80000000; v0[0].max = 0x7fffffff; v0[0].mask = 0xffffffff; v0[1].min = 0x00000000; v0[1].max = 0xffffffff; v0[1].mask = 0xffffffff; } else { v0->min = 0; v0->max = MAX_ARRAY_LENGTH; } } } assert(sp == come_from->stack_pointer); if (IS_INT_TYPE(come_from->stack_top[1].type)) { if (sp[-1].min > come_from->stack_top[1].min) { sp[-1].min = come_from->stack_top[1].min; } if (sp[-1].max < come_from->stack_top[1].max) { sp[-1].max = come_from->stack_top[1].max; } sp[-1].mask |= come_from->stack_top[1].mask; #ifdef INT8_DEFINED } else if (come_from->stack_top[1].type == tp_long) { int8 min0 = LOAD_INT8(sp-2,min); int8 max0 = LOAD_INT8(sp-2,max); int8 mask0 = LOAD_INT8(sp-2,mask); int8 min1 = LOAD_INT8(come_from->stack_top,min); int8 max1 = LOAD_INT8(come_from->stack_top,max); int8 mask1 = LOAD_INT8(come_from->stack_top,mask); if (min0 > min1) { STORE_INT8(sp-2, min, min1); } if (max0 < max1) { STORE_INT8(sp-2, max, max1); } mask0 |= mask1; STORE_INT8(sp-2, mask, mask0); #endif } else { //raphy sp[-1].min = 0; sp[-1].max = 0; come_from->stack_top[1].min = 0; come_from->stack_top[1].max = 0; //raphy end if (sp[-1].min > come_from->stack_top[1].min) { sp[-1].min = come_from->stack_top[1].min; } if (sp[-1].max < come_from->stack_top[1].max) { sp[-1].max = come_from->stack_top[1].max; } sp[-1].mask &= come_from->stack_top[1].mask; } } if (--come_from->n_branches == 0) { delete[] come_from->vars; } else if (cmd == cmd_case_ctx && come_from->switch_var_index >= 0) { // restore state of switch expression varaible, // because it can be used in other branches come_from->vars[come_from->switch_var_index] = save_var; } return sp; } vbm_operand* ctx_entry_point::transfer(method_desc* method, vbm_operand* sp, byte, byte&) { #if 1 // // As far as state of variable is not followed correctly in case of // subroutine execution or catching exception, the obvious approach is // to reset state of all local variables. But in this case we will loose // useful information, so I decide to keep variables state, // hoping that it will not cause confusing Jlint messages. // var_desc* var = method->vars; for (int i = method->n_vars; --i >= 0; var++) { int type = var->type; if (IS_INT_TYPE(type)) { var->min = ranges[type].min; var->max = ranges[type].max; var->mask = var->min | var->max; } else if (type == tp_long) { var->min = 0x80000000; var->max = 0x7fffffff; var->mask = 0xffffffff; var += 1; var->min = 0x00000000; var->max = 0xffffffff; var->mask = 0xffffffff; i -= 1; assert(i >= 0); } else { var->min = 0; var->max = MAX_ARRAY_LENGTH; var->mask = var_desc::vs_unknown; } } #endif sp->type = tp_object; sp->mask = var_desc::vs_not_null; sp->min = 0; sp->max = MAX_ARRAY_LENGTH; return sp+1; // exception object is pushed on stack } vbm_operand* ctx_reset::transfer(method_desc* method, vbm_operand* sp, byte, byte&) { var_desc* var = method->vars; for (int n = method->n_vars, i = 0; i < n; i++, var++) { // // Reset vaules of local variables which were modified in region of // code between backward jump label and backward jump intruction // if (method->var_store_count[i] != var_store_count[i]) { int type = var->type; if (IS_INT_TYPE(type)) { var->min = ranges[type].min; var->max = ranges[type].max; var->mask = var->min | var->max; } else if (type == tp_long) { var->min = 0x80000000; var->max = 0x7fffffff; var->mask = 0xffffffff; var += 1; var->min = 0x00000000; var->max = 0xffffffff; var->mask = 0xffffffff; i += 1; assert(i < n); } else { var->mask = var_desc::vs_unknown; var->min = 0; var->max = MAX_ARRAY_LENGTH; } } } delete[] var_store_count; return sp; } jlint-3.0/local_context.hh0000644000175000017500000000705507236032035016420 0ustar davehodaveho00000000000000#ifndef LOCAL_CONTEXT_HH #define LOCAL_CONTEXT_HH #include "types.hh" #include "var_desc.hh" #include "method_desc.hh" class local_context { public: enum context_cmd { cmd_pop_var, // start of local variable scope cmd_push_var, // end of local variable scope cmd_merge_ctx, // forward jump label cmd_reset_ctx, // backward jump label cmd_enter_ctx, // entry point cmd_case_ctx, // switch case label cmd_update_ctx, // backward jump cmd_save_ctx // forward jump } cmd; local_context* next; virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop) = 0; local_context(context_cmd ctx_cmd, local_context** chain) { cmd = ctx_cmd; while (*chain != NULL && (*chain)->cmd < ctx_cmd) { chain = &(*chain)->next; } next = *chain; *chain = this; } }; class ctx_entry_point : public local_context { public: ctx_entry_point(local_context** chain) : local_context(cmd_enter_ctx, chain) {} virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop); }; class ctx_split : public local_context { public: var_desc* vars; vbm_operand* stack_pointer; vbm_operand stack_top[2]; int switch_var_index; int n_branches; int in_monitor; enum jmp_type { jmp_forward, jmp_backward }; ctx_split(local_context** chain, jmp_type type = jmp_forward) : local_context(type == jmp_forward ? cmd_save_ctx : cmd_update_ctx, chain) { n_branches = 1; } virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop); }; class ctx_merge : public local_context { public: ctx_split* come_from; int case_value; ctx_merge(local_context** chain, ctx_split* come_from_ctx) : local_context(cmd_merge_ctx, chain) { come_from = come_from_ctx; } ctx_merge(local_context** chain, ctx_split* come_from_ctx, int value) : local_context(cmd_case_ctx, chain) { come_from = come_from_ctx; case_value = value; } virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop); }; class ctx_pop_var : public local_context { public: int var_index; ctx_pop_var(local_context** chain, int index) : local_context(cmd_pop_var, chain), var_index(index) {} virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop); }; class ctx_push_var : public local_context { public: utf_string* var_name; int var_type; int var_index; int var_start_pc; ctx_push_var(local_context** chain, utf_string* name, int type, int index, int start_pc) : local_context(cmd_push_var, chain) { var_name = name; var_type = type; var_index = index; var_start_pc = start_pc; } virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop); }; class ctx_reset : public local_context { public: int* var_store_count; ctx_reset(local_context** chain, int* counts, int n_vars) : local_context(cmd_reset_ctx, chain) { var_store_count = new int[n_vars]; memcpy(var_store_count, counts, n_vars*sizeof(int)); } virtual vbm_operand* transfer(method_desc* method, vbm_operand* sp, byte cop, byte& prev_cop); }; #endif jlint-3.0/locks.cc0000644000175000017500000000221707241031200014643 0ustar davehodaveho00000000000000#include "locks.hh" #include "class_desc.hh" // for debugging bool Locks::acquire(Lock lock) { // acquire lock Lock (add to lockset) // return true if lock is new monitor_table::iterator entry = monTable.find(lock); if (entry == monTable.end()) { // new lock, insert monTable.insert(monitor_table::value_type(lock, 1)); monStack.push_front(lock); return true; } else { // lock exists, do nothing (for now); later version: incr. count #ifdef DUMP_MONITOR printf("Lock %s already acquired.\n", lock->name.as_asciz()); #endif } return false; } bool Locks::release(Lock lock) { // release lock Lock // return true on success if (getInnermost() == lock) { monStack.pop_front(); monitor_table::iterator entry = monTable.find(lock); if (entry != monTable.end()) { // ignore counter value for now monTable.erase(entry); } return true; } else { #ifdef DUMP_MONITOR Lock curr = getInnermost(); printf("Lock %s != %s could not be released.\n", lock->name.as_asciz(), curr ? curr->name.as_asciz() : ""); #endif return false; // could not delete lock; was not innermost lock } } jlint-3.0/locks.hh0000644000175000017500000000450607645261161014703 0ustar davehodaveho00000000000000#ifndef LOCKS_HH #define LOCKS_HH #ifdef __GNUC__ #if __GNUC__ > 2 using namespace std; #endif #endif /******************************************************************************/ /* Class that handles the locks that each thread is holding. */ /******************************************************************************/ #include "types.hh" #include "field_desc.hh" #include "stdio.h" // for debugging output typedef const field_desc* Lock; // table of all locks a thread is holding #ifdef HASH_TABLE #include #else #include #endif #ifdef HASH_TABLE struct eqLock { bool operator()(const Lock l1, const Lock l2) const { assert(l1 != NULL); assert(l2 != NULL); return strcmp(l1->name.as_asciz(), l2->name.as_asciz()) == 0; } }; struct hashLock { hash H; size_t operator() (const Lock l) const { return H(const_cast(l->name.as_asciz())); } }; typedef hash_map monitor_table; #else struct ltLock { bool operator()(const Lock l1, const Lock l2) const { return strcmp(l1->name.as_asciz(), l2->name.as_asciz()) < 0; } }; typedef map monitor_table; #endif // stack of locks a thread acquired (in "chronological" order) #ifdef SLIST #include #else #include #endif #ifdef SLIST typedef slist monitor_stack; #else typedef list monitor_stack; #endif class Locks { private: monitor_table monTable; monitor_stack monStack; public: void clear() { // clear set of locks - call before processing new method // (no inter-method flow analysis done yet) monTable.clear(); monStack.clear(); } bool acquire(Lock lock); // acquire lock Lock (add to lockset) bool release(Lock lock); // release lock Lock bool owns(Lock lock) { // return true if lock is in table monitor_table::iterator entry = monTable.find(lock); return (entry != monTable.end()); } int nLocks() { // return number of locks held return monTable.size(); } monitor_stack::const_iterator begin() { return monStack.begin(); } monitor_stack::const_iterator end() { return monStack.end(); } Lock getInnermost() { // get most recently acquired lock if (!(monStack.empty())) { return monStack.front(); } else { return NULL; } } }; #endif jlint-3.0/manual.pdf0000644000175000017500000057720110065643720015221 0ustar davehodaveho00000000000000%PDF-1.3 3 0 obj << /Length 316 /Filter /FlateDecode >> stream xڅQN0+|ÖG@pHOܪ6%i !Yz2Y :מDnme_%4SZȺ(5kZ.\L *9-ƨm8-gz<βu*D{}Q؉#$]`=$F@ҡKrKnv)@01H䱵+^Yδaذu> endobj 1 0 obj << /Font << /F1 6 0 R /F71 9 0 R >> /ProcSet [ /PDF /Text ] >> endobj 13 0 obj << /Length 610 /Filter /FlateDecode >> stream xmSn0}+xt$q(A}mYQ@!B}H!kC\>3N/ XvcQ*JU;Ho%oS!r  7yǝ8ţn'X%>Ha͋uGqPScK<_S;k>˔ ? [9UC?q r!4뜝|ôOR0tB"93͵%Cqԭ9-oOEY3qCɶ>VűjWqa4{8}p)g|ô2n'+ǿ> `J%eA̹[g&Vm% Rw&P]ڢjomY|w>kY&Ke\E2!o1 yP"0߀@rK*4=֭A g@C*R[#U1fpĊʠ=Z$3OAb vj]" Vmv>jpmK̞g9AhSOmDi#tWdYbl5bt95~O{.{ kЍi#:,endstream endobj 12 0 obj << /Type /Page /Contents 13 0 R /Resources 11 0 R /MediaBox [0 0 595.276 841.89] /Parent 10 0 R >> endobj 11 0 obj << /Font << /F1 6 0 R >> /ProcSet [ /PDF /Text ] >> endobj 16 0 obj << /Length 1447 /Filter /FlateDecode >> stream xZRF!-ܣ~`*$Tp*,-lI~nɲG0.Pn[{C=h/zAI3wGԌ I >\HEZ2}vR/GSx\f}Ǿ E$5m¨ln;p#7.udxS)8P,'}%n>^ý/sJDo&n!M>?$⵹UA][E[n/HKlۧIJ$π3͇ei>aw0-TPEOC Tg|\h%x H_AHT㋠0By0\້yΙ -;!web Fnhl2;hmhr 4$~he 5"H"0*6&\P2Da>S|S3ks۳dfOcE}xfTNbtrTNLfjmz8J{ωsh⏋xTXi7B$]S En--ՠfPG4hM *c)x-\LoƋFEFhcTTHv2MaR 1T!դI5;OCi&__ iptMҴB @BllC аo V~,.8'ƈ0% ~J,/*8ؔ76s=cmԈHFf D&xL(Q91%ӥ  AH_}0RX&`%z3n5󨹨Z8x}qϝ)r4:R(x~^%TE}3Ib0+l$ֹ}qwOI[fyj1m@0)B]>0{u30NfT1jT D]x'L,&o*qoA*bhaZO?Ճ.j8Zɱ@Mo[;Vf## SܥEX~狰Z }6UjR+ zZ -ŭ׿R7lr(ȅmt=n-Zơkr'K`FV5Cb:D?]?6VP+Zt<*w9=;[)h H%e2;*.ۚ>cEPDEs4V_`m8W\):hy1KRc*`|Ӄ5V5B9Y!2ɢWˎw l5:..]JsG|> endobj 21 0 obj << /Type /Annot /Border [0 0 0] /Rect [441.93 690.189 450 702.941] /Subtype /Link /A << /S /GoTo /D (1) >> >> endobj 22 0 obj << /Type /Annot /Border [0 0 0] /Rect [441.93 660.6 450 670.562] /Subtype /Link /A << /S /GoTo /D (2) >> >> endobj 23 0 obj << /Type /Annot /Border [0 0 0] /Rect [441.93 625.432 450 638.184] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 27 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 609.662 450 619.359] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 28 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 596.511 450 606.208] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 29 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 583.36 450 593.057] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 30 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 570.21 450 579.907] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 31 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 557.059 450 566.756] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 32 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 543.908 450 553.605] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 33 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 530.758 450 540.454] /Subtype /Link /A << /S /GoTo /D (3) >> >> endobj 34 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 506.577 450 513.608] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 35 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 491.305 450 500.881] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 36 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 465.004 450 474.701] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 37 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 438.703 450 448.4] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 38 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 412.401 450 421.808] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 39 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 399.251 450 408.948] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 40 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 375.071 450 382.101] /Subtype /Link /A << /S /GoTo /D (4) >> >> endobj 41 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 346.648 450 356.055] /Subtype /Link /A << /S /GoTo /D (5) >> >> endobj 42 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 333.497 450 343.194] /Subtype /Link /A << /S /GoTo /D (5) >> >> endobj 43 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 320.347 450 330.044] /Subtype /Link /A << /S /GoTo /D (5) >> >> endobj 44 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 307.196 450 316.893] /Subtype /Link /A << /S /GoTo /D (5) >> >> endobj 45 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 283.016 450 290.301] /Subtype /Link /A << /S /GoTo /D (5) >> >> endobj 46 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 267.744 450 277.441] /Subtype /Link /A << /S /GoTo /D (6) >> >> endobj 47 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 253.987 450 264.896] /Subtype /Link /A << /S /GoTo /D (6) >> >> endobj 48 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 230.413 450 237.443] /Subtype /Link /A << /S /GoTo /D (6) >> >> endobj 17 0 obj << /D [15 0 R /XYZ 90 769.89 null] >> endobj 14 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F90 20 0 R /F54 26 0 R >> /ProcSet [ /PDF /Text ] >> endobj 57 0 obj << /Length 2250 /Filter /FlateDecode >> stream x[[F~ЛQUԦﰏɮRI6#;(e2w5I˶eqn|;W$$'< dz9"X\՛pcNs^=@̠~?`4! Rݸ\ L&Og"};~3aNhB\(nEOe)VS V[9`A)gb:# )ۤ>U)JESWW1kKqic͏ޛaXzH2=S@(:JX*.DJ!;3ģzmL4Z8!wZc:#De%GDs8' /5.&XJ*. 1n歲U_ !}yfV13d[1F%1!L-{< E/XĖcfw8B?\F"Fo~څՇG ^&aB8JֻZR].wSJekuKQ9k-O{9C^?MAcT|KLC@4 I̓Y%c{4h~ڣMk)qQ. }RZeu/,-0O1ӕB424QeL%Nj屼bMv3[lACZSR`+waB J T˖K> endobj 59 0 obj << /Type /Annot /Border [0 0 0] /Rect [441.93 757.138 450 769.89] /Subtype /Link /A << /S /GoTo /D (8) >> >> endobj 60 0 obj << /Type /Annot /Border [0 0 0] /Rect [444.545 741.368 450 751.065] /Subtype /Link /A << /S /GoTo /D (8) >> >> endobj 64 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 717.188 450 724.763] /Subtype /Link /A << /S /GoTo /D (12) >> >> endobj 65 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 688.765 450 698.462] /Subtype /Link /A << /S /GoTo /D (12) >> >> endobj 66 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 662.464 450 672.161] /Subtype /Link /A << /S /GoTo /D (13) >> >> endobj 67 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 636.162 450 645.859] /Subtype /Link /A << /S /GoTo /D (13) >> >> endobj 68 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 609.255 450 620.164] /Subtype /Link /A << /S /GoTo /D (14) >> >> endobj 69 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 583.559 450 593.256] /Subtype /Link /A << /S /GoTo /D (14) >> >> endobj 70 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 557.258 450 566.955] /Subtype /Link /A << /S /GoTo /D (14) >> >> endobj 71 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 546.229 450 553.804] /Subtype /Link /A << /S /GoTo /D (14) >> >> endobj 72 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 517.806 450 527.503] /Subtype /Link /A << /S /GoTo /D (15) >> >> endobj 73 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 491.505 450 501.202] /Subtype /Link /A << /S /GoTo /D (15) >> >> endobj 74 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 465.203 450 474.61] /Subtype /Link /A << /S /GoTo /D (15) >> >> endobj 78 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 438.902 450 448.599] /Subtype /Link /A << /S /GoTo /D (16) >> >> endobj 79 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 427.873 450 435.448] /Subtype /Link /A << /S /GoTo /D (16) >> >> endobj 80 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 401.571 450 409.147] /Subtype /Link /A << /S /GoTo /D (16) >> >> endobj 81 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 375.27 450 382.846] /Subtype /Link /A << /S /GoTo /D (16) >> >> endobj 82 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 348.968 450 355.999] /Subtype /Link /A << /S /GoTo /D (17) >> >> endobj 83 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 322.667 450 329.697] /Subtype /Link /A << /S /GoTo /D (17) >> >> endobj 84 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 309.516 450 317.092] /Subtype /Link /A << /S /GoTo /D (17) >> >> endobj 85 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 270.064 450 277.64] /Subtype /Link /A << /S /GoTo /D (19) >> >> endobj 86 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 243.763 450 250.793] /Subtype /Link /A << /S /GoTo /D (19) >> >> endobj 87 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 230.612 450 238.188] /Subtype /Link /A << /S /GoTo /D (20) >> >> endobj 88 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 215.34 450 225.037] /Subtype /Link /A << /S /GoTo /D (20) >> >> endobj 89 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 202.19 450 211.887] /Subtype /Link /A << /S /GoTo /D (20) >> >> endobj 90 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 189.039 450 198.736] /Subtype /Link /A << /S /GoTo /D (20) >> >> endobj 91 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 175.282 450 186.191] /Subtype /Link /A << /S /GoTo /D (21) >> >> endobj 92 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 149.587 450 159.284] /Subtype /Link /A << /S /GoTo /D (21) >> >> endobj 58 0 obj << /D [56 0 R /XYZ 90 769.89 null] >> endobj 55 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F90 20 0 R /F54 26 0 R /F55 63 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 105 0 obj << /Length 1192 /Filter /FlateDecode >> stream xXMs6WV $xlfȰ͆ EbIQ2%K=ָ͐o-'1$'J;YW1~RB4N ?ͯ޽cm7<ϧ?\2',Nypՙ41&̄d)G l3qGYMgRhU!әH"zzՆV d\j]a ֏+,A5-B_{*aiB;lFߖ`3)̸dps<ȘpKv}t|M`bR^ѵ'B 2K)'Kb'ZSW+i> endobj 107 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 746.777 450 753.807] /Subtype /Link /A << /S /GoTo /D (21) >> >> endobj 108 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 731.505 450 741.202] /Subtype /Link /A << /S /GoTo /D (22) >> >> endobj 109 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 718.354 450 728.051] /Subtype /Link /A << /S /GoTo /D (22) >> >> endobj 110 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 694.174 450 701.75] /Subtype /Link /A << /S /GoTo /D (22) >> >> endobj 111 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 678.902 450 688.599] /Subtype /Link /A << /S /GoTo /D (22) >> >> endobj 112 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 665.751 450 675.448] /Subtype /Link /A << /S /GoTo /D (23) >> >> endobj 113 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 652.601 450 662.298] /Subtype /Link /A << /S /GoTo /D (23) >> >> endobj 114 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 626.299 450 635.706] /Subtype /Link /A << /S /GoTo /D (23) >> >> endobj 115 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 599.998 450 609.695] /Subtype /Link /A << /S /GoTo /D (23) >> >> endobj 116 0 obj << /Type /Annot /Border [0 0 0] /Rect [433.861 570.239 450 582.991] /Subtype /Link /A << /S /GoTo /D (25) >> >> endobj 117 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 554.469 450 564.166] /Subtype /Link /A << /S /GoTo /D (25) >> >> endobj 118 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 541.318 450 551.015] /Subtype /Link /A << /S /GoTo /D (25) >> >> endobj 119 0 obj << /Type /Annot /Border [0 0 0] /Rect [439.091 528.167 450 537.864] /Subtype /Link /A << /S /GoTo /D (26) >> >> endobj 120 0 obj << /Type /Annot /Border [0 0 0] /Rect [433.861 501.198 450 511.16] /Subtype /Link /A << /S /GoTo /D (27) >> >> endobj 121 0 obj << /Type /Annot /Border [0 0 0] /Rect [433.861 468.819 450 478.782] /Subtype /Link /A << /S /GoTo /D (28) >> >> endobj 106 0 obj << /D [104 0 R /XYZ 90 769.89 null] >> endobj 103 0 obj << /Font << /F1 6 0 R /F55 63 0 R /F54 26 0 R /F71 9 0 R /F90 20 0 R >> /ProcSet [ /PDF /Text ] >> endobj 128 0 obj << /S /GoTo /D (1) >> endobj 130 0 obj (jlint) endobj 131 0 obj << /S /GoTo /D (2) >> endobj 133 0 obj (Introduction) endobj 134 0 obj << /S /GoTo /D (3) >> endobj 136 0 obj (Bugs detected by AntiC) endobj 137 0 obj << /S /GoTo /D (3) >> endobj 139 0 obj (Bugs in tokens) endobj 140 0 obj << /S /GoTo /D (3) >> endobj 142 0 obj (Octal digit expected) endobj 143 0 obj << /S /GoTo /D (3) >> endobj 145 0 obj (May be more than three octal digits are specified) endobj 146 0 obj << /S /GoTo /D (3) >> endobj 148 0 obj (May be more than four hex digits are) endobj 149 0 obj << /S /GoTo /D (3) >> endobj 151 0 obj (May be incorrect escape sequence) endobj 152 0 obj << /S /GoTo /D (3) >> endobj 154 0 obj (Trigraph sequence inside string) endobj 155 0 obj << /S /GoTo /D (3) >> endobj 157 0 obj (Multi-byte character constants are not portable) endobj 158 0 obj << /S /GoTo /D (4) >> endobj 160 0 obj (May be 'l' is used instead of '1' at the end of integer constant) endobj 161 0 obj << /S /GoTo /D (4) >> endobj 163 0 obj (Operator priorities) endobj 164 0 obj << /S /GoTo /D (4) >> endobj 166 0 obj (May be wrong assumption about operators precedence) endobj 167 0 obj << /S /GoTo /D (4) >> endobj 169 0 obj (May be wrong assumption about logical operators precedence) endobj 170 0 obj << /S /GoTo /D (4) >> endobj 172 0 obj (May be wrong assumption about shift operator priority) endobj 173 0 obj << /S /GoTo /D (4) >> endobj 175 0 obj (May be '=' used instead of '==') endobj 176 0 obj << /S /GoTo /D (4) >> endobj 178 0 obj (May be skipped parentheses around assign operator) endobj 179 0 obj << /S /GoTo /D (5) >> endobj 181 0 obj (May be wrong assumption about bit operation priority) endobj 182 0 obj << /S /GoTo /D (5) >> endobj 184 0 obj (Statement body) endobj 185 0 obj << /S /GoTo /D (5) >> endobj 187 0 obj (May be wrong assumption about loop body) endobj 188 0 obj << /S /GoTo /D (5) >> endobj 190 0 obj (May be wrong assumption about IF body) endobj 191 0 obj << /S /GoTo /D (5) >> endobj 193 0 obj (May be wrong assumption about ELSE branch association) endobj 194 0 obj << /S /GoTo /D (6) >> endobj 196 0 obj (Suspicious SWITCH without body) endobj 197 0 obj << /S /GoTo /D (6) >> endobj 199 0 obj (Suspicious CASE/DEFAULT) endobj 200 0 obj << /S /GoTo /D (6) >> endobj 202 0 obj (Possible miss of BREAK before CASE/DEFAULT) endobj 203 0 obj << /S /GoTo /D (8) >> endobj 205 0 obj (Bugs detected by Jlint) endobj 206 0 obj << /S /GoTo /D (8) >> endobj 208 0 obj (Synchronization) endobj 209 0 obj << /S /GoTo /D (12) >> endobj 211 0 obj (Loop id: invocation of synchronized method name can cause deadlock) endobj 212 0 obj << /S /GoTo /D (12) >> endobj 214 0 obj (Loop LoopId/PathId: invocation of method name forms the loop in class dependency graph) endobj 215 0 obj << /S /GoTo /D (13) >> endobj 217 0 obj (Lock a is requested while holding lock b, with other thread holding a and requesting lock b) endobj 218 0 obj << /S /GoTo /D (13) >> endobj 220 0 obj (Method wait() can be invoked with monitor of other object locked) endobj 221 0 obj << /S /GoTo /D (14) >> endobj 223 0 obj (Call sequence to method name can cause deadlock in wait()) endobj 224 0 obj << /S /GoTo /D (14) >> endobj 226 0 obj (Synchronized method name is overridden by non-synchronized method of derived class name) endobj 227 0 obj << /S /GoTo /D (14) >> endobj 229 0 obj (Method name can be called from different threads and is not synchronized) endobj 230 0 obj << /S /GoTo /D (14) >> endobj 232 0 obj (Field name of class) endobj 233 0 obj << /S /GoTo /D (15) >> endobj 235 0 obj (Method name implementing 'Runnable' interface is not synchronized) endobj 236 0 obj << /S /GoTo /D (15) >> endobj 238 0 obj (Value of lock name is changed outside synchronization or constructor) endobj 239 0 obj << /S /GoTo /D (15) >> endobj 241 0 obj (Value of lock name is changed while (potentially) owning it) endobj 242 0 obj << /S /GoTo /D (16) >> endobj 244 0 obj (Method name.wait() is called without synchronizing on name) endobj 245 0 obj << /S /GoTo /D (16) >> endobj 247 0 obj (Inheritance) endobj 248 0 obj << /S /GoTo /D (16) >> endobj 250 0 obj (Method name is not overridden by method with the same name of derived class name) endobj 251 0 obj << /S /GoTo /D (16) >> endobj 253 0 obj (Component name in class name shadows one in base class name) endobj 254 0 obj << /S /GoTo /D (17) >> endobj 256 0 obj (Local variable name shadows component of class name) endobj 257 0 obj << /S /GoTo /D (17) >> endobj 259 0 obj (Method finalize() doesn't call super.finalize()) endobj 260 0 obj << /S /GoTo /D (17) >> endobj 262 0 obj (Data flow) endobj 263 0 obj << /S /GoTo /D (19) >> endobj 265 0 obj (Method name can be invoked with NULL as number parameter and this parameter is used without check for null) endobj 266 0 obj << /S /GoTo /D (19) >> endobj 268 0 obj (Value of referenced variable name may be NULL) endobj 269 0 obj << /S /GoTo /D (20) >> endobj 271 0 obj (NULL reference can be used) endobj 272 0 obj << /S /GoTo /D (20) >> endobj 274 0 obj (Zero operand for operation) endobj 275 0 obj << /S /GoTo /D (20) >> endobj 277 0 obj (Result of operation is always 0) endobj 278 0 obj << /S /GoTo /D (20) >> endobj 280 0 obj (Shift with count relation than integer) endobj 281 0 obj << /S /GoTo /D (21) >> endobj 283 0 obj (Shift count range [min,max] is out of domain) endobj 284 0 obj << /S /GoTo /D (21) >> endobj 286 0 obj (Range of expression value has no intersection with target type domain) endobj 287 0 obj << /S /GoTo /D (21) >> endobj 289 0 obj (Data can be lost as a result of truncation to type) endobj 290 0 obj << /S /GoTo /D (22) >> endobj 292 0 obj (May be type cast is not correctly applied) endobj 293 0 obj << /S /GoTo /D (22) >> endobj 295 0 obj (Comparison always produces the same result) endobj 296 0 obj << /S /GoTo /D (22) >> endobj 298 0 obj (Compared operands can be equal only when both of them are 0) endobj 299 0 obj << /S /GoTo /D (22) >> endobj 301 0 obj (Reminder always equal to the first operand) endobj 302 0 obj << /S /GoTo /D (23) >> endobj 304 0 obj (Comparison of short with char) endobj 305 0 obj << /S /GoTo /D (23) >> endobj 307 0 obj (Compare strings as object references) endobj 308 0 obj << /S /GoTo /D (23) >> endobj 310 0 obj (Inequality comparison can be replaced with equality comparison) endobj 311 0 obj << /S /GoTo /D (23) >> endobj 313 0 obj (Switch case constant integer can't be produced by switch expression) endobj 314 0 obj << /S /GoTo /D (25) >> endobj 316 0 obj (Command line options) endobj 317 0 obj << /S /GoTo /D (25) >> endobj 319 0 obj (AntiC command line options) endobj 320 0 obj << /S /GoTo /D (25) >> endobj 322 0 obj (Jlint command line options) endobj 323 0 obj << /S /GoTo /D (26) >> endobj 325 0 obj (Jlint messages hierarchy) endobj 326 0 obj << /S /GoTo /D (27) >> endobj 328 0 obj (How to build and use Jlint and AntiC) endobj 329 0 obj << /S /GoTo /D (28) >> endobj 331 0 obj (Release notes) endobj 334 0 obj << /Length 406 /Filter /FlateDecode >> stream xmRn0m,6_+=8@ H#ݵAi @0˥e})x*!:aሢze<ߋ}HU@X\Z){""E.gI3@_R >}iV*\Zi']*ˑ`奯y 联d:p*=R3V`ߵaif|݆Q9hz[r4kj9=17fku[M%OvZ!-3cuWKX̀M[ Z>N-*qإAV'j fjq GHxAe݈ r W6*'e cWmRJ7)X秴10\#Ajendstream endobj 333 0 obj << /Type /Page /Contents 334 0 R /Resources 332 0 R /MediaBox [0 0 595.276 841.89] /Parent 10 0 R >> endobj 49 0 obj << /D [333 0 R /XYZ 90 769.89 null] >> endobj 335 0 obj << /D [333 0 R /XYZ 90 718.722 null] >> endobj 332 0 obj << /Font << /F1 6 0 R /F71 9 0 R >> /ProcSet [ /PDF /Text ] >> endobj 338 0 obj << /Length 3526 /Filter /FlateDecode >> stream xڽZKx%M7{^ SCKHR@R;WUl8`a?]ՊB6SQ&2)v~=Ezn^xGr0(U ϫ$IU/s\@ՙ;@N ;fQ|&1}(ةVf]F) p+qEϫ,FB'ҁAC_W&MuIymðm{@L`L$'&~0=M.&}wm1F ~EF_xkCNxCb6[ o "Mdxꓮ|ET$:Y'qww(d =X@GN YĶKjgBJg_ny͂p3гl>+ɒj#-nKvEJ}ʅR8{%'d-[Z6̀ j<ՖlhS&`ENH)$^P `૫flqIM+h}=Q&e=|ԉsH(8zՓ2ZlOM[ďO>ѷQLkdpㄙcs<.$v$? 'ȈUy_g뱈4-L?]ӟ 1GIB?p̞'+>AUvVi:d}*CC ¿u˾?+<#I 5v]yaՈYz` ~}ŵCbYGŏQ#,xck`Tn5<IvڌWfkη^%tEF[mG.HU +ѠSaذb?ԆGYx̆RJ34c$;*6R OӌQsJ8zTQsMT]'NjfA5 ~#6A ۗ;=B[qf+<Obhne v @д m8݊ a{!),? u t݈L)Hm)cIh,M2_c(j{لdptXcf4+R=Ok><;g4 qLQݍdyF郓^BNhkM{G N6x%_֍p:c?(AR:ci:g%خbl6FԔj:-n/8bAb.b Uяx`)p;:/Ĕ¤5!ۄҴ%A*5-gv\rXVVP̄$$ݝLZL(B-F.۞kŎUL3_a'; C<ܥ.tB@sC%NM\aࢊzFpCIUPd3Ic >bǁA;&&6GRcI~0-4=!=L.BI/i&Y{qW[ ߱ENrf"cƴoź,6wڬQS|pMbr r!̗w&E d{i&:_to!Fm /`sDKp"58tR_H/UbǔF.mI"3亭Fu1" UCve# &'jc3Pq+%QWT (<9 3q> PPޤWӽ!i} ,*}gYP[iۺ9uJ~h !M"}PJ,*-,66Ia-#DoJ 梠o0<ĥQI=* L8 q@z >ՠI-j,aJ"W LscKxF'RsC"t`.u" Ho8DbT I9L:> vW΂`w]/Liu0xq4.&Z]eWpWg,Rda(%I3Q4)^&^A^5l O2m4rh|j0;مm$ȅEj<ԉVKr}G z掍Rq^0cE~F`*͐D,BIib "7ԫlz-V94r8tH z^'ا"hb{1u.,!$ Ɂސ; !~怕}VL)S\xcmIY6DVt?g])Ua:g$PIţ_rUN6Z%4þ l]"mxra_s؃~Kw~j{&EkTGoň`}iPEy1$ jY'Mtj iFۣR֖~z 3{tR7aubQTDJa{Z6 [m⋾ȸ֍!tSm?\\ՔYQ˛Hw9tEmJ2: m-jE=*ҋEfkZI~ б% -22#t(F~^C JM1&~*R+ jS`|RhO3(BrE] b1AzkM y2W2I2?Ö ή$ B/c RK5^\yz]ìv_ɧMP*;I] )D#GSk Bjo5A~BÐo3sv`P _9tͯG{Eh-^#c}{&KMgэlOmó}2%4eGeژ@\hpx,hǣŞQӧd/WP~΂վ'6|_\U@1]G]qX>N<3?aFQr*E"wsAlajm7fendstream endobj 337 0 obj << /Type /Page /Contents 338 0 R /Resources 336 0 R /MediaBox [0 0 595.276 841.89] /Parent 340 0 R >> endobj 50 0 obj << /D [337 0 R /XYZ 90 769.89 null] >> endobj 339 0 obj << /D [337 0 R /XYZ 90 722.069 null] >> endobj 336 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R /F54 26 0 R /F55 63 0 R >> /ProcSet [ /PDF /Text ] >> endobj 343 0 obj << /Length 1956 /Filter /FlateDecode >> stream xYKoFWTn]>RA@ $==5-@SDD"]j-?"EՔ-8hNEܙv+}?}qbTb|s觳g:Pi_/p=ջZe3u^31^b[ke#֛ qtEάsfcW~t5gf]tLքOBya lcwkx:M e tڌvZq CeyB{p0L2HG-VW]/!gBVD9#XXps8,R0+>Ь7ÚV=XifBڽ8GQ/(+ W,#!IXO4PW vF9:xtep ]ǩ[F*Ce|8zXtGN7-::aG[r"x,Q6O[+|9)@n|ȜD%JQӘ3JyA bBi+k" ѷ`n1p@|&CP( $dˆfq MM{-5*롹jB L%FddsꀗVGQh(!dr2PQ4#WЕbb_ I@j2 RAy*"Uq#py[!A2nO wRLVdLkS2ۢy1D@J4~eI`$[b}&(\X(~\c!bIC?c- >Qs Uɇ7ߣ/Ј{]oL5C?b}~ص]Qw] ݾz}ÛOF} eR)%y6DE7O;s؎%Y=Pe˔m;nJД??N}(ܝ}p=K$D@ȶC+mɻD0gyZj&zɾA.~3zL.{rbR" x,V8s,A[~b∾v=8^v'yO8B/ y6)7w}C*9>_^7SN|DIrgc%TOA3 L&78_i頏1G/D N"ِ&=1{$BP4ǂ;Ƹ49mS_OO)w<٤ 4Q=ӄ +=Q!TU<~o.i>[ZNTsZ+m%We3Em%#옘gXլ Tw[q2RBȪnGnȡgz|ϏռXU1xJIٽ]K> endobj 51 0 obj << /D [342 0 R /XYZ 90 769.89 null] >> endobj 344 0 obj << /D [342 0 R /XYZ 90 718.865 null] >> endobj 345 0 obj << /D [342 0 R /XYZ 90 612.966 null] >> endobj 341 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 348 0 obj << /Length 1979 /Filter /FlateDecode >> stream xڵXKoFW"%E1 8n $h"遢(i"?ZvIY#Es|n<~zx؋G+x\owl2j2Z;d]( %FdDΞ |idtc'+_߁cvhcS֮}7LÙv>&QMBgu:b綶;Z`QƒI'+}%eY++L3ƖYȝhnL`GPT)wCvc'Gor Mi:,g)j-ߥS"ˉ;O9YŢ4qn0F٘YZƵF,P;mء9`cBBA\ˌGZi9!pk|g}=U*xC!2BՃ\軑73W,sXC4g忰Lk i3̾0j{( F8v I >,c z2QHH+Ot':$ IlC(C#.$|ÒFԛ%3JtUdENX]6Tr,tycՓZ wR=ooo|~~"v;endstream endobj 347 0 obj << /Type /Page /Contents 348 0 R /Resources 346 0 R /MediaBox [0 0 595.276 841.89] /Parent 340 0 R >> endobj 52 0 obj << /D [347 0 R /XYZ 90 769.89 null] >> endobj 349 0 obj << /D [347 0 R /XYZ 90 624.917 null] >> endobj 346 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 352 0 obj << /Length 1717 /Filter /FlateDecode >> stream xڵXo6_ὬriHYVhmɞ=0l чGshKxw<5dIB/&y}1Ňۋ?E,ܮHq#۩ bMCuƘnݡK0!tɦw8 }8 cmt$ӿnP1q/LPۦU=U5CUl``g/;4mUSs,G߮)>dyrAqVW{yDFV,ZHytA_m}EGV:/dM1p$`Wr5sओ9q,>pd(.,PhM =6~u*u9% ZC$`707lyBKb)Ͳ͸{들 b2(6Zq]ްԩN/qoJ';i3'q .pj^l߽cuuѨac;W;'W'Pr< yEOG 5 {GP3^%C^qOO؟m ]!k]&4b&"fPVb:2uf񜯀aם#` VFQo5Bqh{(c_>`CgxHRbKbA52Zlq!"s( *-m*9;}$p@^]a?;9wg~'Wfru$ <<٬r #8]nקig̕A[k/gȌCN,3K(@_)K㯖O%ߗaJÓ܀ZeHlDtޱ^M±7m]eu|ԇ+WϜӏo/s1(yQTEsYU'wa3_;endstream endobj 351 0 obj << /Type /Page /Contents 352 0 R /Resources 350 0 R /MediaBox [0 0 595.276 841.89] /Parent 340 0 R >> endobj 53 0 obj << /D [351 0 R /XYZ 90 769.89 null] >> endobj 353 0 obj << /D [351 0 R /XYZ 90 622.525 null] >> endobj 350 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 356 0 obj << /Length 1337 /Filter /FlateDecode >> stream xڥn60P&"RԖA d :=Ȳl@ ``Ʒ[NBIN, (۫B>̮n?ʉ <'%]uIW/EQ uZkaXub(W%.u\2}}*Q.oW+iБ@;_A&5b)ha.]U#ZI$C\5B̀*~u_[<=k3L@}?XXV:ȵJy 0T*T,mh&PȖ 1=3+F )6L@ ^_ K,z+_սmjmcV1ִh?"cI ֬b{džcXiaD-ف%0E)ǀ`g@XBqNV?Nѫ{UhQ09bv]\3[EA:=r?Oo#= {ZވmV $u fax|Iqh&+ эUM+jRF$5󞺥2-"@4D_a3uØQ m3M&9J$ ^3pUly,ڊ_<,zP[}Up`a6|h?L`œްղ6%wa?rfg} w9B~+ U2e7M|AkZAetkAµOLqC1GEa(lwD%z%TwXJ|ܦTJnLLp,Hq([؋=TڃWh@ìls(c.b:dmEݺ(ˊJjNcQTFW+QJۍ҉XfTp\j2̩l4=pU=|[zۆ b`(& ྾f1D4vЦ"U/6BT]*͸ņ)baiƃ).>g(g7 ׯ}ihIhm֙r݉\̉OPLwo3l/; іk۟b;&:`]:Ϋ@|%i9ct> endobj 54 0 obj << /D [355 0 R /XYZ 90 769.89 null] >> endobj 354 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 359 0 obj << /Length 667 /Filter /FlateDecode >> stream xڍUM0+U+$aCwJvLbH1ekgj 377_,)iHÌnd e euLr:  !_XJN>D$=d@8]:%]1\HQ<}^XX!ϑK .˒L :+{PZ/B1"3`S 3{>AGάV;H|WS͚<oJ  $9Eps~r0*D E]m!uNyJF/d$yLj7D:UI\ĴW4!Fx{1)I Y~NF}Vc_xVf<')H5U c-ވYWY:hw6"ߵ=\cO+ +kBM\0s`W*5˅^m5! ƟG:;.9:7:V 5{H :G$kQbus sUX&2Y@1}Q 3-o}٥TSiq>rʮLWV_}u~րԁmk:H&iȏ(? endstream endobj 358 0 obj << /Type /Page /Contents 359 0 R /Resources 357 0 R /MediaBox [0 0 595.276 841.89] /Parent 340 0 R >> endobj 360 0 obj << /D [358 0 R /XYZ 90 769.89 null] >> endobj 357 0 obj << /Font << /F1 6 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 363 0 obj << /Length 2865 /Filter /FlateDecode >> stream xڍێ}B&H:E@5֛$XȑDeHz~|mHjupfΙsm p4,VEsn޼ WaA]Q֡7&zE]o8=^ W` ZZ_KuծU 덊0ow7?݄BJ)=ml~xhNWa$:ZoBI$1Mm%QHt[@W. \aq(x% X`R%yzk4 ̓F}vC{>9E1֙W"TH/PjO%!M rCxZS:Q^G~у[@uz!3_E 0$t[^%TH),~ ¤{D%}`IJ HyrVPVGBrD-i7u ~+AxkZޑf %Ilhn(U"%v4885'Pku3Vh tabBQޏ@|Y  z@헋W38z;f(J=Cg+#| =<+GU fWt$'o:)x,2j 9!jXb\Bj&n] x4Ù H(dé ?8DeSX0*aD Oa)lx@umhDňY-Y.i+o6]T|'k-:DŻ+8QX UM]#e)%*Y&e#T p0\ytpBCqd]^e;p'HE fĜ` o7, !Zm`0uc] =k#D>*}39*@G8mX.:Gχ{S\<dZ(G!9z)Jwdlz|0%A#Xx.}ȅ()d/')H;9$h+iϗi)SX}]R8;\A)tLd%>˱5&y˱MH)@t FΖ*?3C*GE:11윩q?$f䇌<ޭ?]#*>:,6U̩:+zŧ C_QX−(/T,\JLpYCW %[zH=?U훝c(rITBlZ~>kˏ u}`ҴkgorYym'G?I>w/vX\}X},h >P%a]ej'bAzB ;^~6@Ϥ(=kqY [o~-X¿ ʚ͐m/(]?tf6{3߱m5Ԥ-Tãgf̑i9d4;6c=cU" f @pRu3SaBeeL~_A#{Ag r[ J Ґ; [8k]|uԞr*e#8u*9,~6%Jp*;IKi 0GA) 5A "V9q)0+Lq;m7/tIИ2|WaO\Q5G<dK(̻I ~N|kqsB֖*Wj˗+ ==uxȗ*s ]c,{ z4;5*W"$ō Qo_LyiP05u̇;`{YHͅ=}LE 5&D#6l!,Kޞ¥~=I* iu!<ܤ\ W%GMj1Z1 `[\r .^exM,4;W*lH9yk*>5XH^?lO,*gkR߄EwfS!+= jI' ^WHX=uq/[˩[$]j"p;@:^9IgG&NC.4UPmL'`S YY͋ao+GTH BNT8؜g'Teudz⸴Rn7qL#)r!X0, yiλ0HC-Zipb~bmFDpB: =#Ec Ilp6nB԰.Y13MXK #f!.L߬b|RO2/`' V2F@rYIn|yn'gIM*Jp|ͩ6A#(> endobj 93 0 obj << /D [362 0 R /XYZ 90 769.89 null] >> endobj 364 0 obj << /D [362 0 R /XYZ 90 719.378 null] >> endobj 365 0 obj << /D [362 0 R /XYZ 90 408.187 null] >> endobj 361 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F54 26 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 369 0 obj << /Length 2417 /Filter /FlateDecode >> stream xڭX[oܶ~X%ZԫH9 1Ї4(dIk1${3vp8J+UDLVݙgWg/+sS/]]mi\Vn;CNH)j 8PZ0Z#3Z5k8z#OtWgV%3F+rޡXP|w]| EM^um. +o VTLɑR+VXP6VIi%"0e {>E8OH}j[j 9;6d+Q}o89QO@= $5; VˤF)cQ7q,˶镕1ȷV|H#7 CпGo|vDn hM ȃ1#ydGw<T6Y]p*/P?w҉ut[ ~ 40JBw<_%D*}-nDb#d2\'bsW1(<)UvmҹZ'! E(t^C|>Jǎ#CWap]](]:=Gg.$Ͱ8in/;˗$ʛ`j{";= K vy1K0G pB!`ܧQ7 h' PK r$Tb5p£0Ƒ ;e$<}stE]#tkH0 }$paj%3kZ) qeVFC^~o% H !Oa\z^{#3bg{A D; qXDv^_CĂeaAq`h>XtKjZc]]@m&M$"7 @P9>(ei(8yCu!'xw4,xVYM׎CAf@VMm<0U^U(6>YQ/;Mn4ءn1*3WΗWRg, ZhiA|1TF##6}-w]2^5 mG#A*rv<յN쟂" 86LCY9]vĄ#&bUaJepp# gZ5B|䜣]Q>Q?[ r'8'D~+ `=}LLzwk`7x Q -- ַPvҕlyH&&WɫPTFOTlwrvXm{x(ii;͹EI [4a"%]ү]zSRg^ FWb9ge WhG'#|XT({0{Ԃ/_>J@*o9d'!^U]?1\|)EKcMۭaxD zy <i0Mk*<7UιY# ax&@yH'Mp LLu5>έo/7Ź{5P7KS.-C.pA b )4Fd'w;3=6P!۞s%zIhmy'}7kg1r\Ps=.dEoI@ÅBL-*nnS4lvT`%T{"Lp3otAAA^<_[QOo䚬YJ5/Mhɀ~O`X$nb-&RU&oxٶK%>}qɼ2wњ#endstream endobj 368 0 obj << /Type /Page /Contents 369 0 R /Resources 367 0 R /MediaBox [0 0 595.276 841.89] /Parent 366 0 R >> endobj 370 0 obj << /D [368 0 R /XYZ 90 769.89 null] >> endobj 367 0 obj << /Font << /F1 6 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 373 0 obj << /Length 1383 /Filter /FlateDecode >> stream xڵW]o8}ϯD*,@WZُmf”ίaIڝQks}ms+, +EHEVyz?[f?!,ṱ[V'M΅=#n(lu3w|߷GogBN5c\;݁SUsܑ*-L,NC{5k+݂|gr̸O+<;QgAІ0!#w&Ըk{/B e.\ |pGnIڗ}mIgUgqsK&G'BOI701Mx,Faz`kX=֛dR ȕQn:ʇh'BC@l;/&8}dk69eФ@YSOe$GlMBhF0ɂqg1m&5t6EN-j8p8]/4}Y; ?TD V>8+w!E5PG4)MW MWFwʩȩ˩&)6 ;5-䬚eNw]_>SNΰ)Mmm_ϽvOW7H,ݯB_R t xN! *#v:_Ѝ62mK# խ\WxQd.x#=K`WlÌ֌nѓm_rw5m&B!:g\O |摲]zoB ,} "t P6Rv\⠕ D`8e-}'f"lk (Xʳ?y_93u}/ul[<&a=KAPic[s&#ɚ80#I78j5k*F!lӤ7GNɟ/{nIaމrY]so4GA Nf CscG>{G]e;fPZ #Ss8YUS1qΟy) LKQN0c,OʯŪ/ Ȭ/D&#L1Ї~T~0}Jt#"HW +V2YqN` ތSY0td hCu#Е)_ ERg1mP Zf#:mw !+n?;ʭ8%֚A1endstream endobj 372 0 obj << /Type /Page /Contents 373 0 R /Resources 371 0 R /MediaBox [0 0 595.276 841.89] /Parent 366 0 R >> endobj 374 0 obj << /D [372 0 R /XYZ 90 769.89 null] >> endobj 371 0 obj << /Font << /F1 6 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 377 0 obj << /Length 3113 /Filter /FlateDecode >> stream xڕkH{~ł#e|n@'+wkfW?<@Pu?uWFwyBw7~z՝2*ϴp_+R=T07 Qk$Iw3IPA͉Ak}8e@kC\LR|7JhIt qf阨< ^ʰP B$Ҍ@I\U 3,4,I`3?릪;ap:yW ~O"to;߿Oc"4,\z(O1O$.Ez!>΂{iLO8Q8 e>Mx0J*哆K& .c{kp8ikC2Y5*ʌe1ҁqi`y4F|L%h+ۙDp7y i"t8`-F ֞7)B]<]&scAQ*:NU5. =ݤ#*i34Iyl*IT/ Ki-SViWjLǶA<D #x21"bR7|H_:\,BHUy0F蝖70lg^;agC<oFtO; *StC%RfTހ5֭ 8)2MKsSfE$U3WVR4 \iUD`<³:}%S3)0'P&^Yyܙwi Y߉?Ku<Nȟ7+McǦkrj {I\>"/Ù񮝱 ޲A\ZҨ CM&$Gˆǫ5 Aa-{9'˞ຽ=RBAվuN=Sg?8L ]CH` [S{cCQ[  ٍ1-OƆ@`@ym0HK2&*e*q+1m U71UaPaZX*B6%iy< z~O) c%סvbd>ZÁAq1R=Vmx)ckUiKB HRL}1 ԳD\&%*c5OT8 +DW$9J +_ٓfRW+`1v1W$r⺻Dv)zyvK/=20!e9'Xq"f cbjt+ܶ]f+Hvw[6sc,)_.2N*J}zk-*"3~^f3&YsBױĚL١ jۜ&W_D1f鐻^~ߢ5Jm"UHsI k`!h\Uw 6r'w՚`o;ZC,pKP.2bFV̺xڢJ U?@Z!l_N^k< }%pCカL2E(V*_aY1"~7;#jUUd"^ )%8-6'=*ƖWݍ̨WFȲ, nGm5;)0?k:S(GKsij?}1q DF\B{{K8% ؁X;.FGaxC!rѕTw1yi!ܶ =mJI¬HVj2P!w;T~R> endobj 378 0 obj << /D [376 0 R /XYZ 90 769.89 null] >> endobj 375 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F54 26 0 R >> /ProcSet [ /PDF /Text ] >> endobj 381 0 obj << /Length 2042 /Filter /FlateDecode >> stream xXYoF~_18ȃ l$n7XPd0Vm!@$jVŸ6IHMVln*)=m))T^no > pJ@B͙`һ>OL4w{mbu}F9]BPoM*>>k{=ײ\Z,{/KLV~A!beۭvvkܠOmSCҽeE_3w-oS 9ꡪCbL,@5|N+;9,mXp~AR}:^i$܇$N kGwp!X1RZ{nkeWb ]ֲEtZ2هjԦU*RQ=Oe_ Ӊ"C@iŎ.uԟ7g53 ru*7G~bUAnEJq-Rl #/6s.# scY/!h'c\ne h18"ƂG HԐc0uŶ0Rl 4@Pϐti)=645(TQ\:tR!Y(@_L1#ub|Ņ o!kHn֢*~1Ly&7w]voGB⯙ Qj֗d#/(:MNeIBer:T!0 Mp}E/:z`IŸvp'& x;QM1Nj+:޵Șƭ9!̪h J@H#8 r)֩7ƻ9 Ԯ1pǡ˅t-zVlcxÛ},޳IkrHN;"2X6]>Rs>c2 : ݥfpg'LǗ[VI.txaxx eB1\g)NND![Om MUs' C "#Aq{MW9э@&\4WĤDnȝ"QձX9i!- Cyx8#`xI%"AxF7H řT 5T &:TBWggi(E*9y6t}Gs I!>c[P/ktJt*Xd=˧aq<*KEQ14QZaq-ɟ%|6)C"R(r* "qYn >nh~:1l3vʬLT;F`A i,(L|[.O#/b[W>m0@p ~t|h+fP|k^zuI"B}kj 3ϮiX:w*E2R Nr@5,bY(aqQJf.u,fNOna Yr{c9n./Y7V hmup6Y9z9#No qZ̞])]֧oAx0 'CxlFhfS-fNY7hɶG_őRj֟>P\3@x7[//su,Cb84nBgS"օ'^'@/~ #ͬm?0+ג羷Ma0eפkaeAj:X?dGݴz y6nQ$q&Ȧ iRWv u> endobj 388 0 obj << /Type /Annot /Border [0 0 0] /Rect [235.802 275.718 413.217 286.627] /Subtype /Link /A << /S /GoTo /D (Synchronization) >> >> endobj 389 0 obj << /Type /Annot /Border [0 0 0] /Rect [502.606 139.516 522 150.425] /Subtype /Link /A << /S /GoTo /D (Synchronization) >> >> endobj 390 0 obj << /Type /Annot /Border [0 0 0] /Rect [90 126.366 248.718 137.275] /Subtype /Link /A << /S /GoTo /D (Synchronization) >> >> endobj 94 0 obj << /D [380 0 R /XYZ 90 769.89 null] >> endobj 379 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R >> /ProcSet [ /PDF /Text ] >> endobj 393 0 obj << /Length 2654 /Filter /FlateDecode >> stream xYKoFWHv# fߒ(II[Hdwo &]]]ϯJzß&ͬl)obt滟F*M~8O[NGZwι0@BcU2Iw[G/Lݚ,w&:q-8k`.LܝUR#0 8I{&3L.c?Gnb3rLj嫹xi ykgQn4Uѧ _Q׏^m i|T?5(SgaTe;mٜ& B=◈aSZT3Ǎ_gV_PT+k@=*麥\E~)lص<=>LrY7"IrD:TbD[G9O&3(S9Y5L]ua"iG9\FK]}>yLT)֩LĪv?nN{YL0d> >xPM!] _ʰ` nj<]ҤJ\xM@oUܩA#VnXǾBCSO#Okˢy>\dc~y(r Hm2gײOیFMS!FI>DÄa"N2e\kw%!De.4+}U+*5*7A&;LM\ARcf y+ׯBhr*p;NL4^O 铍:8oe%NIڸO[A`F%Eݩ3IWDTzBR6Xv}CO8]M9D_!+h v>jMiԐ7=/z KBpW6@pEĽd7->3)o?\›! lf!C##p+v @#r8=?1W6OgQzEZ_ hqaFB(7d}z-pF6.c]?/*+w  Oa $clUA3 3 AeLX=U7Yg΋%thJ E <Dќ)evǬg hƢмl!C01`\Be^dXUeEdjuLxPKQ  g6Uw06;`=;v {ƊFùԡ r|ԭ{4&;tniB+ {/Pza z ,bxa/lUm; .Z*k 4Ҧ0I Y3/_J](8mo0*E"◊A4ӀA"*K!"tO S4X1a@g&&*)sx]|03+H?h2"7qЪLXUL3-IG~ @7¨iij0hr8PiGˡ,Fb> RW!9`}&)& *C@!(Ms[Bk?r_u0'ܜqHc`}HZv6<==,G9$Zpՙͽ[8L;ebV۵F2ėr  |1 y Y_SO;e@K8E`\ f]ei͒8's>M`dw47+| ,Z%Y) `T_ e}U`b{YEWOdTa(te23\cֆQ@w9a? &;|s[z%.fvY76c yo o+ʈap nԓ褚[r^c7RazwXF#7J}4?]` !L=v\zPt80*sendstream endobj 392 0 obj << /Type /Page /Contents 393 0 R /Resources 391 0 R /MediaBox [0 0 595.276 841.89] /Parent 366 0 R >> endobj 95 0 obj << /D [392 0 R /XYZ 90 769.89 null] >> endobj 391 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F55 63 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R /F54 26 0 R >> /ProcSet [ /PDF /Text ] >> endobj 396 0 obj << /Length 2342 /Filter /FlateDecode >> stream xYKWHfn|1䴻EIʓO"%MAŪz}U-6ˍM-HV66z:RETlq)=t0VP.4.N`$D1V%~+}nwq06.mw}nEkb\(B mrm@kBϵ6 u7مIj\OdI0TNU[VcǟjDG|X%PHBiDX1B <4kYfN e+oXP3:xbZ_pdӟ$/E*M3U)HjPKnyzP-ǏOgH X8 Ӛly)D %7d#8 t\eY Mt^` |KC|D#GQǍQqfJv0Z(ٍk6PQ; 4v1@& >`;'NG}#.7+ KzeewONB̏11p; uײ>{};EaVC%EFl"ni^D \1Y_}9)صW!9p8Þij5Vn!e}*Aȇ# 5 AB4DA!jJd4VDŽOϢ-ڋAA#m*"/Y'ĕ!:xrܵhP̰NJk`ᙘTCzdzEՒzITW+T?J3^U+-T8xq/^(2jΡf7Th*T$'}%Uж Z 6 uA$6AXY~ͺfp6װ Wչ,UGzF֩Jk.v("E 2aVYQvd*+0ش^G\i$UٸM'HcO]Z.u޻7J}(Zl9 6K݄ U#34'Y9iE: r0ǽ[Pђ!$f>}F6g: J͋#M7w[XI΄噱GIr ibsf5^ ~_yWIF~Q]z6oP$|S8T|;uHB?7bQ/~`x-O}IZqIbm:ỵIHa; E)%[ލB%ho}Fܜ[-2t&!f?>.\c3nqdvw,];!y!=HEsSՔ/0Q.kqCn5dnrƸ񷧥ΰfdVɉgsA(! 8v Њ;%К{gNE gx͈>(IÄN@"VTm4]N9,y8m0{l[׆7Q;NZ.i*p> kOTp)8 xm! ]RTq?rEwΒתE_A\ve9AWH OTat4\ JsW毯E6fWq`{!5L%b.ZSbU-sd*2|XR#'6 9Mlp4& C^~䊠qA޺.t¯%ZՖM7MX\a͌Y9?0r,Y)afj#k}!凩wkKV )~\@y Ѻs#0Ʊ rs9U Uy%x;0^ϒP|gE Df D[(zt]ᢕvjFYDqK|15aW{FAkBo( U)]̇ԉRB%Rw@o5&e-uTRKЎ{b;/?[w󋶫N3jE-n[uU'H#,0 A7Gˏ&@'~Yihq^~g;T鷗y'NL4.g(}( N]En 2> endobj 96 0 obj << /D [395 0 R /XYZ 90 769.89 null] >> endobj 394 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R /F52 77 0 R /F55 63 0 R >> /ProcSet [ /PDF /Text ] >> endobj 400 0 obj << /Length 2018 /Filter /FlateDecode >> stream xYIsFϯ 5M  Uq"$\C{F(H_t˳)BHQ^F'E2r-rϗYg/g/OgO_ɹLDspzvW  "Q֑~1E3&Z Q IWLzPEtD'@TWBѰNeIpz2t&=/F+h bk# ").Z ~ElS.[Ewp*$Z&}rE6%`'T&7,`w:j.,mwVd@ zT[8miIx<.RlK# cYAچ\vۉTX)V.{EɆY:D7xwQ* wݔɅ ݇dKar;F e|3BeڏﳷVh=Nm3I TÄJl)W$.ǩЅJ}7-8Xk)0&x@WY{vB^mMOﭥHM)s0{fd jF  ٽ8\&F1|gs*pr99ܒ⋙znvΘ&h ]lV}T,1{N.ӈoyC#|HM,ufXwG%ĽklU~p@,&6#$w]CK \2cEReP*nE+E1'0.y=o ie"2!hǐ6I~ b.q2hVruW71-ko5LBfcv3Jµ!/KԖC~~}޶A=K_bBFAi Jjrr۷xh(~&G 8$qcj"f\< V3ѺOքk0h6VQ>~ Vgѯ9'>K`XW_.#姫p7mog8endstream endobj 399 0 obj << /Type /Page /Contents 400 0 R /Resources 398 0 R /MediaBox [0 0 595.276 841.89] /Parent 397 0 R >> endobj 97 0 obj << /D [399 0 R /XYZ 90 769.89 null] >> endobj 398 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R /F52 77 0 R /F55 63 0 R >> /ProcSet [ /PDF /Text ] >> endobj 403 0 obj << /Length 2921 /Filter /FlateDecode >> stream xYݏ6ߎ þ)+t[iniˆfV{4@rU>~.&?ɣLlS"xFEa囇=MZ/o $Mfgfg0OS ~-T,dsPu7;jkW{?=+q! A5QKW vOcnS͍gW6 nYs\J< (Z4Wԅ0-3695Av7Ɛd`ʮ_*U?2Q4˨*aqpZnAa ◪,I $W#tGiyRҹ:I'+Mx4xcxa҂ q Q 2RRM LʢdDl"҄Y1 r8.\1HEmiebT H4":e <]m:  \p+z8UGC<"6 #S7L\AeaZ&oy2^cgX7dqBz}`ATI#o1VE$yʑ]՟XܳKgmHO:M!6 ]LLቶ!s2:+ πf%IwO mokDXwEǩh:g97 DzHJfPfF:LeZ2IR/3d IEPWv F N 'F%J=?) ? xIֳ(cQa!l@63šST@:m:#<*ʕu@/l X̿n:!c~d$ EHIݭgN;/p3(Ld7("|aG ،Gv'XْW+klGL).@I*㡨85fs~kZdB/I.Zd++;}dz:`m:zTw׮g22SE^ @uɁXh'ZSsO΀ΗfDZ k'ʮv%ff2mK .VS; 3#AHdV̅Bo(eTz3 AC1ҥJwl|4dLWL?n#r˵3?#ٲNC h+<-YhFσp2028Rқ/]0_g4!Sd/Yܶ Re aa?[ Gus`K/58s"|=Xc< ̔K$^n윸H$#oG&Up/ֵ$Y>9F˒+O(iq@@ISL%`š}R1$^-,9.$밆. (yƞNr\ D (jrIѩ ~Yyd<ˁݯu#qA"3HWo^q/;rqY|?5"`i@sKsKX^o`-=^Ts\KG((F *KP '#_TO]iSqJ &pzYkŀUūNemo[>`=޷DTZ@1Ȗ$K_35+ՍRI &m[$F4,s)5N۶3F$ 3-%A]@`D}{ VLUOnP(*KF.sv,M%&~쵞%%h?~[8{d`{=NӍgrEڹh1O`䎋RwvڢwkQeV;$3:YزȱGHC2.Lģ;"]6yo5[c&''ݪX+~6o_gw8jCH", rb(Dz.56a1~ȡCBG.YgHXjC=}[?qx4fwGs̓%BqZ/H_9!"6p ,69ڭj(w3k,jM,]-i-'%FMt 7f j6*ua*/e5E禽yn+|/g8Uɛ?vceYOL6CX ; RWPVY}aj:;*փHK+67Ԛ=ݷt ZЪCeR7+BG Ujx?('{3+Yt;ckH̖hendstream endobj 402 0 obj << /Type /Page /Contents 403 0 R /Resources 401 0 R /MediaBox [0 0 595.276 841.89] /Parent 397 0 R >> endobj 98 0 obj << /D [402 0 R /XYZ 90 769.89 null] >> endobj 407 0 obj << /D [402 0 R /XYZ 90 515.058 null] >> endobj 401 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F71 9 0 R /F100 384 0 R /F101 406 0 R /F53 387 0 R /F55 63 0 R >> /ProcSet [ /PDF /Text ] >> endobj 410 0 obj << /Length 2927 /Filter /FlateDecode >> stream xYT:Q$(.i&,˶Hr|( X9<8 4':Ϗw>}x潺W?y_Jy}Z+]֞~Zc{苜ILYD@T*L~uROūxDCx΄sCkb?4R?Q^}YT(D=IAo( + U֡>4vQh(88͎]snz7@u_mdoȲ"dV iv 4Z?5WmS Ύ;71 KAo.*+hFvT:{!ݖ@=T@y֗Mq"/0ޑ`JƂFCQ\hAR]ʪRNꝌ6u Q5PV&H֥r)`J3rg~ɇ3Bl[Lqһ&@_[sJ8IH fw/)Q 3c#hbeCփ7t\8MwO!Nɨ#XB-"Ӯm9x!M]ߞhO/a~登; 9b"LalVy ΀`bBd5,ݽF3C:4擝c)G(iCQ34+ë$~)SqʼQgNݠݸ %惽p&H)[rG4N[Թl*BO>rC2̉Fh{5@+M(*Zh=r2p⯲>e}ކ{G[DVotހu{nd7IR)|Y2T@S,%Q˚_| F7v(1S5]CToHʙi!*py[Ϋ+8xqaY}?/^$*g S"eG*.i!o ]KUL%S|= <̑ߚbQo'݊Vfξ|SH6J$ea^5B9Ë9l *bA`)1Vu5Q0q 2q[cbr#T/L .?)\w{IH7 *7J{iPQd1| *ѝp.+Hv VNQJ0w'GxAm`cRi_H*m Fd}U-Br47C4tdкwA>R#|߆'UA~@ʀFڬWWuvⶒ!J@- -[H c7FAG>OHHe+ ȄUj43b]C*'܍ 9NR.!xK9N$ 7mI1 8yϔJI&Q]klSd,  &;y73@3_F@@C}7u"CFAS[I4K:ë7V+=g&p80%lbD[I2DE`1=n/v#6BZ?H[r?1:܏}Ð$;x%HpWq!uLnZs*_(-}۠$+iS6&X b383v ^aZkvB[a"!Rut!c,q<%ݳC 0U5Mx6@7yLE eЧ"0[/j;| ewsషW]?FncSG<ߤX?tᅄH0B62i ts HFd(,y;[^/xM49}AIac@{%eyϸ7 6({b-5dOGŖ<c;1|킪_L|'Onrw8\zdWAn-SA3ܤY8Ÿ!]`hmE-+@·#,?} a'R}ou E "O!g6iKg" VOIq=(6`UuU3$ y6QɐD)B"Ո;x|SYI|wLɪI\0;PsO0˥o˸&o|(\%[dܶ [/c ::Jendstream endobj 409 0 obj << /Type /Page /Contents 410 0 R /Resources 408 0 R /MediaBox [0 0 595.276 841.89] /Parent 397 0 R >> endobj 99 0 obj << /D [409 0 R /XYZ 90 769.89 null] >> endobj 411 0 obj << /D [409 0 R /XYZ 90 289.978 null] >> endobj 408 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R /F52 77 0 R /F101 406 0 R >> /ProcSet [ /PDF /Text ] >> endobj 414 0 obj << /Length 1760 /Filter /FlateDecode >> stream xڭXKo6W=ZKJk{n4i 큱D2$ywJlm"@DMEHyC !0,|S:!ڋ,V˷hME*]|ɦyu_csgKú޽m[pwΡ,"ʃSٲ?U>z ;K:b+TȇsRoӒ'ѵEzUiFb B 6_9)Ϯ&qHG閍C'6,Q\ԥ0uu7Ec5+^ l`Y? *,aλ]5Аz۱N2t}&alM>dػ/JmE3GOX̏Up}Hj"4~~H @P#A)`;LR:BV"r]RTKC`;>_;/5[jScD՛%k6(6a3X5 Aw,{K: Do !itIXsYDj;!hu}9rN(6^k(pZoR֐XCkx~M2TGT\FP{c³6 sf=\ȶ8#]g &Hetq\⥘GsrN" E" h $F%!r!z~lh_kE@Xt6\Ne`a) M1Qn9Ё4'ğ>4Seو.v`L`:>7|ఢE8k-]=B?3pWQ.K$N ITP(x Ozf qw`- 0$[4F=/p,䥨{8^RJot;ӞM0⤱T}A0dS`vuCП=3)B@ʛչ #.~$csZ%WyeQ̨0 DE@G`2 ;M:FK.= 4Ұ5XDV3[dT M/tT$0ƴHw4 3U )rN?V[GX R^ף٢04U3'}L:]#dYΤo'<-AHƹA 3$g ĉ@ )R&*yD3(V=(, ?@aT8k6zATQ^d6 FHIGO0~F~$nh~zwZ6x =I&|OJbvk3DZ6k`΅eFf[רϭZl } zRrosԢA(Iߡ! <=(ЫpUbXkf$!.5)C1 |Scc]jW8Q-:HON.oy0>\J `'QFdJ$0=sZK1gg+f#˕xYfkN CZq [w*~3^]endstream endobj 413 0 obj << /Type /Page /Contents 414 0 R /Resources 412 0 R /MediaBox [0 0 595.276 841.89] /Parent 397 0 R >> endobj 415 0 obj << /D [413 0 R /XYZ 90 769.89 null] >> endobj 412 0 obj << /Font << /F1 6 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 418 0 obj << /Length 1899 /Filter /FlateDecode >> stream xXKDWD\%nqհvęqvfO}UmI<  4iWWw=ˀ2i,̖"pŷWoR*'nx탯WkU^_EQ}{@-0G_lu+k޳0%}y2asO۷o~ϔV[EtkJhg7>?A:UE->_\54yðm +<~b_ũxRk =q]W;kiRt-W:_6(cܛ8l358(jp[%߶HH ,p`v<;9%v,67}ԝW m,.Z

TA1OD4H"f \p <"ocɪ-e⵸Xؖ88>J y@|c# 'N9QG((4?ȇy a?pfv7Lʃ<<X(Rk腒 A%ݜa9&{^{\yԘ4LڛH$gY4k^4M8]VV].%*PTEtGSR7{h6^>)a*Z_8f6wѐmAz h^ԛbTUp;dJT$۶?p0gxLHIT\j?,C=lqaq" <)ETL( TB|*=m+ "keB voe<(>qiRߛz2##z=i톋KѺ8ڨII YɅx,3BGkGeчʪM؉4I ]*h,0wf+%2'GyIje 80ZW!eO~!4fmn^:'łn"!awͶixJqŶa࿝.~<'Ḯʍ<6[G{/ %2+ Ɛ9~_:U][T`=>Ms64;A]4MVQL/G"550Xz;¿-;ϟEXݕ۹-撤t^toNtAze}Sv*5☀41L_bC%&+dj~Z#Ƌ;ʡWrR hja}0FcʒoZԂgoH\NJ迃jX qH/.}C(N',m@E20 7Gy7ñؑ9Cff> e:xW'2hˆ v-jR"7d#8-g$Cqm'wݍXq;uS;옽1DIv6+o1$M|R;W?LMiF t s%_lԃ;! mS ၝw74KsNtͷՈg*Zn"la,>D5-5l;AfCui ӀrH.^5(^ACo-endstream endobj 417 0 obj << /Type /Page /Contents 418 0 R /Resources 416 0 R /MediaBox [0 0 595.276 841.89] /Parent 397 0 R >> endobj 100 0 obj << /D [417 0 R /XYZ 90 769.89 null] >> endobj 416 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R >> /ProcSet [ /PDF /Text ] >> endobj 421 0 obj << /Length 1605 /Filter /FlateDecode >> stream xXɒ6+KLDb68͇)Y8%L2_^@h+8Dhc~b(V^l= Kd<]Pt~J7p̝r'J)G]CQ9FXuΪwL+]Rn WNN EH߽^~]VG&*=D'^,4j9M{; d&şҲ|g,%Y@{~|kGyF܏S|xMNgۃ-3I ϐ-0#sdz$:o:..>s{d\;묮e/[ܺ*۔U܈h*kF랼|A^*O$^("Hߋ#^0? H/ "{?YYZpּS8@„G/-ڳ~ ,HyI$"^CC)#q0"<zA&ms*!؎u?(kZy: f Y?I9 fm9? QZ~I &1AD~lHv%Cl9ʅ A7=- `:=ۜjW>4-TU؝ oKͺ)ٺε]lHANЗ?|9b;O(k4v-析U9JL'ˎJ-Iog,#QJutF2=L3E:yeH e -$c<0btAh9e WVf#abC3V:޻Qo!Xir{`ZLHKYMO+=j[c7Mg-.aɀIqGr{6M?v|&)7@YE PS$g2w^UPي-\%Y>>P5H쑔|Ai`JtClAvf̬eVP@}^CY իC.1Fc/slÊ"N".[T-(:o @"@|gp`քe.y˪hxB۰alL=(,czS; spqY>I'T,<PeNi>uja>mfk}gէHvIhGѺ*9->cYg{8bCIfj&%ً1PGendstream endobj 420 0 obj << /Type /Page /Contents 421 0 R /Resources 419 0 R /MediaBox [0 0 595.276 841.89] /Parent 422 0 R >> endobj 101 0 obj << /D [420 0 R /XYZ 90 769.89 null] >> endobj 419 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F71 9 0 R /F53 387 0 R /F100 384 0 R >> /ProcSet [ /PDF /Text ] >> endobj 425 0 obj << /Length 1762 /Filter /FlateDecode >> stream xXKs6WR3C|&X7%aK$JTl'$v/~{OoƍM<#OV}s/0[;Wv6LSOǼL}w>uW|g,.u>щg[`*DN;jj06)7=,i򑃌jz:ڸhMAhUjM:Ym&F9WI7˜?"``Į .pfgzJ~:zE& 6ZV1n?d36K9˪ ߳0(HMNئ{bgcc c"7 ?oygLWzWGFσ(HPFAgN73ؐh>Gn>W!:XM[LH`Z-:Ea0?#W"WpvdTIX6UҢ(|vƊ%,.{G['@5iU;ۚ]3ۚy)8`yoD ((ئ p͏ JXjbAO tҲՐxMNS]5A$tPβΡj^oW)|Gob箲4wٰpH`(KI KUu/ĎGF|k״=9:u)T`ɥ\䓅}Rer#/BVehncU}9c *P(r<?"?7?9F%h۞_>O<qS臁2|7Tc닞sObֱm-y v &%-q^J'a\} Ƭ (=_ĸj&L\ׂmZ/vYc{4a0g} MYEuDV7E5< @Ȕ굩Ͻ :\Ο9v%Lhٌ"v93>x+qS4 1#8`)dzPmܔz0few[܂ҮN~5l4LtX ֯)EKjDe^VsdL%jβ0c#ib;h`*=Ihߌˉ-tFԇY^>dh%/z(8ta6{~vc̐F6›Zdx)4+{ .-xzx+E3! A~❣$],!ypNJ`/ n{IpJz~39"-Rs`ܒ:]3׀:{0ϠX\iïqv:kgqi5!1?_jxكްLL):xD;iZ薖Q>僮O!v7U|+=P-EZ-+b.p6r& F\wLOU]jADݻ7L,7[ ."ъ%O=n_E"ݿE.{]zo8p}%ٛ> endobj 102 0 obj << /D [424 0 R /XYZ 90 769.89 null] >> endobj 423 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F100 384 0 R /F53 387 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 428 0 obj << /Length 2078 /Filter /FlateDecode >> stream xXߏ~_>3(Jb AdAe[=YI:}dɖ79\Ep8Gs ?y*0ai_*iKOoq{mX^XAKkkz!6[EW xgz"/&O*g#m9 1Wm Gb"ЛyUxv^%Oc|Araڊd1Y(ŶǂjYdl+[ia/PUsXWśw]^\̆ó5n%6y,r_݀E"u>R(eH(Vo% A_Gt0}+63 tGGʏ#W)tUc\ᴃ)=lc^EC҂$iF/fNLzv(G r]ZDn*(vo0Bj7ԍ}yQp߭ѽݖHua=G {%ज*4lЖհ<|'h6U+qXnWU9F;"7LHn≞{sMy_9oC!+RP{]pR% {'Y5-:@rg9 f;싫Ӏ٥\vnu8~7"30r|O6T }3}ϾcKfCQ *T2߸-?#=%#x q4Kf;t.bHXTrgvA9tYSVz'}# ~ [#wˇ|CPHuNK|ْ0c]5ڜ֙t 8x).1큃\=alREx4)ND&4*Hb \D4_|q3[ef2Q o.2LTwdVLnjF'|+iirL$(\ؗ2W'cPx9+|%y#%X?qq73]P"tg0v\TK~$3 B x=lQ +޴qM\='&J v2 ҶBZz%֚ JsL&Nm}&Sy=\8+۴h]EBee;NBCmU e&x_rT_.;ampY|+pՐ26-)p_ķ@K 4 zB^$#VZ(ՏMo{vZ<{l(^#h_~/<7,LnRCr5.7O쥰KǂLH; wwRc$>Q! V}"g^P0p 95=9Bm4BRo}rׄN#r 'lj\jkVWsWUUdiyBY\T3ÿÿa v^Q[rJ2[=U|LQ[ E y nZn ?ч5Ej>R-X.>p}N/{_kF?9;W}cGclnjnﰒDE6 'Vqr/V~)V=~hiy|48FL` L^KZvuj5k(Dp@q0k0Rޯ䒺zיC骩O.Z>/bkTKs5Scdg,&oբ&|[vWp4|YX}3r#P8?~9h-(endstream endobj 427 0 obj << /Type /Page /Contents 428 0 R /Resources 426 0 R /MediaBox [0 0 595.276 841.89] /Parent 422 0 R >> endobj 122 0 obj << /D [427 0 R /XYZ 90 769.89 null] >> endobj 426 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F53 387 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 431 0 obj << /Length 2098 /Filter /FlateDecode >> stream xYK6WVb/Rhb ˲V6΃6-Hg8~ߌV-%WD.]lDlev\HjHdCzRA*hVkcL`^ib 0ftyS@5q  vFeǫ zq:UW=">v/!amc|PҲ@B5ֺj]=w:F8y3jloCHe˲-՞.KZx.A-tv6YXnuH:^^>g^ΘS,+F}lpc?+/'.>&K7H $pzh@ec+jb4Цernι7|BZ'Ͼ/ʒMܻPXؗm͛ȋoSZr ޴#;F_믂 ^)tšLkM]9:@ǻ72P`g4Ǘ@ADinKZDzy{F V61nǂɻSS]L^E~lю"RNp.y"5eJMU :JΪA}2VQz,oRR=U6J5ayKطVQ"&",yO\ߵPg;AjsrLL-ЍϹ=Bg)ȊMl#Z-(xwpf7ppQ'X 60斂/ԩkNl~U,vVK5ݙu0#iOt ,} $ /xN'D^!z=RbRnRvq<;L|leu_zۃä,k;D8UYWԞBrL 76@h6/` !_5slQiS~F9GIX\e.MGvuRJ)ELm?V*~#9kU@pC'1ll0 <%6-f|C>9|gu'oab (p4O8W" d"nÎRDQ4o!R/ kjB(f[t'E/pO->Xa,<3:E3dcsEp=/"yp#囚gU0).OQ&~dD_HDNU8T9\́%XD&'7Qg,f꧙'F &a+qkW 'iWCO:؍DJT$)S193xLfLpNc-y +l872O{a:o6#~ ծ֣'Tk=t{(p"$u $>*2wo΀.f1endstream endobj 430 0 obj << /Type /Page /Contents 431 0 R /Resources 429 0 R /MediaBox [0 0 595.276 841.89] /Parent 422 0 R >> endobj 123 0 obj << /D [430 0 R /XYZ 90 769.89 null] >> endobj 429 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F53 387 0 R /F52 77 0 R /F100 384 0 R >> /ProcSet [ /PDF /Text ] >> endobj 434 0 obj << /Length 484 /Filter /FlateDecode >> stream xڕTn0+t*(|X Cκ=PGBl<.\*h.w!! .X"}B1)hF$>$@6ILI* bRJrs|0"Ic!15BV /+-h ɜ\ \&GA"LJ%dM tmZƉqvkiӺ 8qqC,%LԾ Gv9aׄ.q´=3(Wn׌^{GgU6 *S1n}MQ?1NXny{ñy3-b՝Vw19$^1~D J%=!Zxt/a 7|C[l'3 Ji0s^7VȖ#d1<ҩZczHEX i]4?^tU7\]9cz_C>S *endstream endobj 433 0 obj << /Type /Page /Contents 434 0 R /Resources 432 0 R /MediaBox [0 0 595.276 841.89] /Parent 422 0 R >> endobj 435 0 obj << /D [433 0 R /XYZ 90 769.89 null] >> endobj 432 0 obj << /Font << /F1 6 0 R /F52 77 0 R >> /ProcSet [ /PDF /Text ] >> endobj 438 0 obj << /Length 3191 /Filter /FlateDecode >> stream xڵZ[ۺ~_Ked)ꖷ&A (v>֖Jd˕8{~}F4h +Crf8ͅVSEtqjw/WonQ* cv[*Rbq`^/Ƙmïrkkj|aB:Y|+%{X 3vhcKS2HALfej\2I50v_G~}/H,#S c: uoa *+w=gD/t [dygR%FA~zD@W2 kRcSYIʟB4qۡj@WšJNO!4ʃzUv-'i@NnWCYoQ/ȢvEp½vSo]Qy_ǝӠ?>0K<o!#dEF{],8NFo5u-xޖ4}x|@k1m=$#aRxnf5 =? dP4C&Bį0[rȊƵڮvǦ$e'^]6S*" z c.urxcNƃVf&fmҦDFiu[TaȦ`^ !I?3Nx|Z%a==I衇=dH,gV@[?R*QjX'Èrp j [y=(T  /N)^C* NXmP]Pg5WwWsKɈ+&lD>StLA6?PG wmSbAazy,LaIA(!њ 3 e٥63z^QJ&Eu|ڿzNx dSeo)a+7sATZtC' 4W9GTй>[u.i@#|^Rd"1MPq":̣ljܓ Uo0ͦD7 ~YiI6g,D7P/} K%akNpSFG%uB k\.$<>sm' ny>c{n!{Ɨg&F|YnQĘ{=,YFљ@J~}N@OsSx M>܉0JSߐ^tDs&{4˃ (J04|ڷ#P-YٜHW,4\ $FkDnZuA5VrXM˹xB_/*~vYj{lɅvՍKKp: HX퉚@a+Κ؄"IP20Ha ΔCNrH&"~%O/7T|f#FoyKL]r iGL,2pd]k?0ٗya$t: js80}ZI<GiО.,'3 49?*Wa%W2xfPgٿ[ N:t2fI4LQ`~iH9]u 4sY9<-~P=j(i#>&2r kY d6~0 0SP@E8t_ĐT0(]K4ᅙ6lQdzINI,t}jrJٝ"dMZD@Q=fHt{{u>ArZ{˗~x.70Fʼn-(S0j3JiCOƩTAsr+?L $,vr?*|ޟ{v4b'+'؜"Gdp{s7^OM GًewI!aJ翿PL'jΈv{NB)ʴ7 r?>zlV,Y0?Jendstream endobj 437 0 obj << /Type /Page /Contents 438 0 R /Resources 436 0 R /MediaBox [0 0 595.276 841.89] /Parent 422 0 R /Annots [ 442 0 R ] >> endobj 442 0 obj << /Type /Annot /Border [0 0 0] /Rect [332.852 241.263 514.727 252.172] /Subtype /Link /A << /S /GoTo /D (Synchronization) >> >> endobj 124 0 obj << /D [437 0 R /XYZ 90 769.89 null] >> endobj 439 0 obj << /D [437 0 R /XYZ 90 720.985 null] >> endobj 440 0 obj << /D [437 0 R /XYZ 90 630.357 null] >> endobj 441 0 obj << /D [437 0 R /XYZ 90 552.322 null] >> endobj 436 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R /F55 63 0 R /F53 387 0 R >> /ProcSet [ /PDF /Text ] >> endobj 445 0 obj << /Length 1307 /Filter /FlateDecode >> stream xڽWKoFWD5WoM1CݒEJ"J.IU~}K24ݙy~;^#/ʹtNRܼnnn'#Gc[~@cPk_NH=-HkjT1~0ְ!s$ۼnh߅1"/4ȤAj"Җ)!VN䔢Ka@Cumg ҿ6?G^ m"a{B2;mPfar&Wv8_vW]3> nCYyBx(ΉI"8z%̗$*[{`n_vE BUh<՟"i⒅vWi_a~du);ZƢOcUV58Cjc朙22cjZ.t#Z<#PțaţWkH_ȾPgbe%VB@F%B u'+2O)Rcڹ\FtyMJBMuII||~MeJpF+^Zh]_vwTcSew\GSs;EOGe2Mpx82 c%J*(q4R+HURJd&RxYwt v;ˮ]Qti|Am[bqm yZ $꿟4i.G-D> endobj 125 0 obj << /D [444 0 R /XYZ 90 769.89 null] >> endobj 446 0 obj << /D [444 0 R /XYZ 90 497.312 null] >> endobj 443 0 obj << /Font << /F1 6 0 R /F52 77 0 R /F53 387 0 R /F55 63 0 R /F71 9 0 R >> /ProcSet [ /PDF /Text ] >> endobj 450 0 obj << /Length 2500 /Filter /FlateDecode >> stream xڥY[~ׯK5֔ o~:v:im" Z$Ɣw76#R \Ϝw.3}3Z:/gx]0|aC7\,],^z4xja=7W' 5d|J&Lhneq 0Yp?{0 D^C)`4JX%* fb!*CR"%wuj,t@ in>l`Tf| bىALUc;=kkL#p<Z曉eH^!CuȿBgmy^Zpab&^}ՈLDi֥`r^ QK5ܿ] S24_Q&`UcU@M6+&ߢ% *G\Hg^dMHay$;^NDoǨ(lDȁqA>k[gSӳ 8b̫)k`{Rp:{*_Է#wA2S-]@g@=@R>W$|oZ:<eG,,熏s0OhtN8rӣ* %aX\gHCBxSbfnɢaJpHbl'8lyJ/xʆ/S#}'Dp= T  qHK( NoC7%݄*Ǚ~[U&eWK!XX Lɼwp;S?ZM[97Klh_E ۖlO %w9CUSnbS eCVy![,r ЦG uP,uE&H4@C"h뗵Wmwl)_퍉J".PA,%ZEQ6ɻ \y` nZEy8Vޖ\Jەצ0p@pd'\Lb/ú QBVnMk)vp49odE G`@RkDr@ KM(0# ׁ *Z+,Oi]7Z:#~߹א@_KJ^9V:vDH|Qax9oya%cT7*q<ǰ'%mܽ,=`IzK4C^IL^r}gp803`]TTb)$ DZyM9ڲÁ|/%H6 H]H"ױ;7Iwع=ߢJ.'M?5:{<26.|dBPzs(a4U}DE@˧@u:(j|ceO|=3_ET~UKճ:V#xJ7ž7]e*' [0ŻNI+\*q'$?:M4r YoǑH~з PTi N|5տ8=`l(uàl|XJݿ?\d95ꁣAWέAV^%d\8Ro> endobj 126 0 obj << /D [449 0 R /XYZ 90 769.89 null] >> endobj 451 0 obj << /D [449 0 R /XYZ 90 722.069 null] >> endobj 448 0 obj << /Font << /F1 6 0 R /F71 9 0 R /F52 77 0 R /F53 387 0 R /F55 63 0 R >> /ProcSet [ /PDF /Text ] >> endobj 454 0 obj << /Length 937 /Filter /FlateDecode >> stream xuU[o@~W8$20(lhӋMk4>`%`\f49|3SwO#? }'bsO:]ɾtȍɒ ISܬҍ%E[e/?,[)%&yItAnKe{Á /~'ۤ'7{=soL6l9TdpG!apg$)շq#E<\,\%-o襜z"n?i_ڏźC/T&ϊm۠yRn PUeݰf@]+vgfkخtpmެuR fB6Kߑ3lY *oҒW,y*(l`Ds+[auLq 0O=ևӶ+6]Tb `J=KcbϓB4|6>B@dUJ|ڳPg1r|R8;Gpaժ?tx$P[4E_7D\[W5-ḣ" JVq~3ϗx,Z*{g(yx%,oYfI)E< Ď;[w-9׬bt#c1szy`"њfgNpi5>4z% -[3TwP?!Q -|8̞K1L1LOǁ7 <:N)p@` ᑡXRpX׍~<ڔzKoVQX\7x VfMN&4Ўi&(U:8|l=5f%bie7)zQ.[dĮ)Z%~LH~ufYw ;:ܱ!.8_oJ֨Lf!vp}߽8c2%~]21q_=u=Tw8Wc{ endstream endobj 453 0 obj << /Type /Page /Contents 454 0 R /Resources 452 0 R /MediaBox [0 0 595.276 841.89] /Parent 447 0 R >> endobj 127 0 obj << /D [453 0 R /XYZ 90 769.89 null] >> endobj 455 0 obj << /D [453 0 R /XYZ 90 722.069 null] >> endobj 452 0 obj << /Font << /F1 6 0 R /F71 9 0 R >> /ProcSet [ /PDF /Text ] >> endobj 405 0 obj << /Length1 1002 /Length2 3137 /Length3 532 /Length 3813 /Filter /FlateDecode >> stream xi<}#e e+JN2l\!%9ɘae&+""A/BRk7Y2Bd"$3յt|W>7wqB$.X]A}"|E&VV$¹ ]&㈄h2 TUe5E%5Ee.KpX2 +5 h{$XЋY ,n8Lx<`/`$B ΍ 8#C;@}1~A/ 4) 0-b<\0S".xSߺoqO3GI ?ڂ͙QC2s&xA]A I~7$`~70]=SCs?-x#(W0=$\ f"nD %eM")\`p0aP<0{ I\_`hH䯡*O2sDs0߄`z? J 0?`Z!̳?"#R? dUX?dV~1ip(!eUfMg%8Mu#1E07/v1A7D7KB2_cxgZVP< >Gnn.;oZ<`oԳ .P8Zδ4ZynnBy-~mէ%ޱDdZͰHw\ӧ#K$,|"#F&fr)B̏3##ay/eG` zUq}0awD˲㌎2WOuog`=gK߭"$ueVo\]̫7OPg7F!]2bKv0lOL<}g!ߕmQ۷w?ThDO] ^ۛ$wzg`lYCy>ғv \jq|DMt C+,ul`MU8-_8#gMn8R7On$Qk,tލ0`r&,)3|t dQm'|_ąIƾ#6_3 3 qhuj9JNEVL/cJ[v{njm3_zvDuC,*1M|YN#JvgjVy9!qT2HP/2#s']U˸7c/xm$\WFzg7-{c:{ʨeѢܒYv6ǂN{}:"fm)z䇧A› N",;śvè}Hz54qKVzDgi9TS;5bEMА^+tn {YI]Hk. )R[!,w9eN"O&U }ku[v >\KUKhMtiޜVk^jkvWDG)kK ms0hKLo%Yrз M{ūeI.UYdpz(R,ES6멝 ̱u-e"Fu㭱Јd ⼙Q:aM0m謡^ϾdlƠXqnI46k#pv^8P\'608<}nc_AA@1'!71j]G*gjٿKTOiSH6J7 zt{ڧ.z$dˢg< 7<+&* tS*EdD!}G؊ Spz:b5Rcq|P̫KٶϧݘXA h9ei7}7k5>ӓGF&;Q5ZΰCs^}*5"GQԛ[!m+X6"ҪiBsxdd‡6HC$Ⱥ|y<&W2NS\sYDO:!<'&wgu7s]Q=Tʣե4!>%sgQL4:qr lsB>Ք}KWpTMƉYN1kXfݩMmeP6sE}WvtcCRF[hy1D?uխҢu$Cu#qiUb ORti:'IW$'={K|o?qڞkB/\KۖU7xHF(i ݅/pS6jS٪֯3.w j7l.>9YGzL<~~v_+ץ)vIJxdCZӤ̥ʹ'޷~:vI#osJ눞id-giUA}Y~ϣLJ# A4LB<yendstream endobj 406 0 obj << /Type /Font /Subtype /Type1 /Encoding 456 0 R /FirstChar 40 /LastChar 122 /Widths 457 0 R /BaseFont /BCENIQ+CMTT12 /FontDescriptor 404 0 R >> endobj 404 0 obj << /Ascent 611 /CapHeight 611 /Descent -222 /FontName /BCENIQ+CMTT12 /ItalicAngle 0 /StemV 65 /XHeight 431 /FontBBox [-1 -234 524 695] /Flags 4 /CharSet (/parenleft/parenright/period/a/e/f/i/l/n/p/r/s/t/u/w/z) /FontFile 405 0 R >> endobj 457 0 obj [515 515 0 0 0 0 515 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 515 0 0 0 515 515 0 0 515 0 0 515 0 515 0 515 0 515 515 515 515 0 515 0 0 515 ] endobj 456 0 obj << /Type /Encoding /Differences [ 0 /.notdef 40/parenleft/parenright 42/.notdef 46/period 47/.notdef 97/a 98/.notdef 101/e/f 103/.notdef 105/i 106/.notdef 108/l 109/.notdef 110/n 111/.notdef 112/p 113/.notdef 114/r/s/t/u 118/.notdef 119/w 120/.notdef 122/z 123/.notdef] >> endobj 386 0 obj << /Length1 1145 /Length2 5071 /Length3 532 /Length 5800 /Filter /FlateDecode >> stream xgޑxdZؓ}a;OO@`+'{sR&jH!|Tiί$^>JJA 4snq9/ ~ 02ҕ9 |zE1ЧqVdgrzynOڠ7,f Bj=5~^ne?_V+cQM51kvrzJ7f `˥K̢{N1Euzfk(Ϻ ˔姧 ތQb#a/\!;L2zW:d0d©l%%3wWXYd<-V)2JlU Ϝg57˭k~+3zKd„DH14(D)ßR"?dGryws}#,!}^I0MIThaIxm~~)d/B%}rP3,3!փ# T[UW7ڈJ ;'uVXUIt/0+#wuh6#Q<Yul2Լn^puH2KM{5y#',X-P}3 9W?o슝: |"Wc)8f֏a};!$"9qi (UKy]nz܋x@FIz-]t~̴QqIci&E묚)~abŁJ-B'n/]+Rob֜ 64|^󱞽(,-i_޸l}BD"Nf&&K~ zA/<M)v?R]*A/&Mٍ`$Nj^6eYK"TϺbÿ4o"}/ݩ{ vz%[2Zpܫ(8|8VY'm{d4bL H w~}ouEK a$Y*g4' PլY ?7XՆmtVy@eUUu'}VW!a)$blC*meDqPʼnfjXZ { lT(8]͉ByNhG5+~[c^ypCgH^>Ӡmf>f۳qu.VBukD_SC)c}(Z"Wi6ЄD6 ~iey~ec#dQ.Is7uӗݝԻ ?@LU/4n$z9f>8"jR*Kl@ J $ʨfmUv(pIz2=,F.{ vlirs ҁ]6 obK~TrXCR %-ns!clDEs_o{ɞ[5W[2P=#Mǁ sN-E,{1'SU^B>)jdl3՝lUp' })y8YVS`w`'8&"\CXx#*÷R~.$DdEMJ,搵g,zEq^@05~Z(1ѷ?S(Iow?̬__kcd@:qP2c/ݗaN߯-Bن 3/-na:s[:zP*ަWEo%O 䢑΅eH;$QS fl4$j.Tdf)k\7Nz*+l25yDeQ&'1:?2h֝jE"i%K7zanP8FlACc|=-\fxp;ɠiASwhj0]? #0c[q[)#g|kk沋$fvIy& |obt/)LB (QlV;/d\\9i23FGv|)"-?cD0]TuE 5Zjή&q=2z]$?=ɤ?t\U-0p8Q=ߛB"Ut_r70'&>"kWC@!DV2gIנMb,×ӲyU\I&_5PΕ\2.~o1SbqbM<ҋ±b0g9S`xIul~5Y)H,PjqL][X i(}7RxM//uP\kH+NV vJ=dk)@qTK֬b1:jsHܗ{~w )AȊOMZRF2~w:C!gJ$\D f~ӾRJ`?XcXj/Ȣ;<8H'TO;~F;cԬIF:$| V2kKg0{= Sr #39XK/|j֢ރ9ߗuZ@T6$U;(F%2dax;>Y1ʥm;`ik*YHNWȮVPXc×,D MSv+lv|rkek0}׎bu xe'2yfS>-VLJ/0cmoeQ`3?+H_\)q;uu6SyHQH|*OsO{q4UYa3k7BIb5Ǽ10(^gb;bu. E3]8'7Z Ɠ5`:`+w%ѿyhA_69SW W=ηCZsu 2\x#Ȍ iX.-2k A?uH4f~×L5}串P=:|14 xf״?q3 %$b`ַX}bWǥ~r#83@D nj7*~iB]91V 3%E=L9z=,uGGXLؗ< mneAo _I|@&:-3wGK~%d*aMMē\~"Q1:TCgoTK1KXۗ7&#Rxl#coSmF0eo)Wp?t)5Va> endobj 385 0 obj << /Ascent 694 /CapHeight 686 /Descent -194 /FontName /ZAGGEK+CMB10 /ItalicAngle 0 /StemV 108 /XHeight 444 /FontBBox [-62 -250 1011 750] /Flags 4 /CharSet (/fi/fl/two/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/r/s/t/u/v/w/y/z) /FontFile 386 0 R >> endobj 459 0 obj [556 556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 486 556 444 556 467 306 500 556 278 306 528 278 833 556 500 556 0 428 394 390 556 528 722 0 528 444 ] endobj 458 0 obj << /Type /Encoding /Differences [ 0 /.notdef 12/fi/fl 14/.notdef 50/two 51/.notdef 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p 113/.notdef 114/r/s/t/u/v/w 120/.notdef 121/y/z 123/.notdef] >> endobj 383 0 obj << /Length1 1086 /Length2 4373 /Length3 532 /Length 5091 /Filter /FlateDecode >> stream xg<\%$bDa ;QGm SC A D;ADA%w9sN9/}u?w7{Z9Xu 6*M a x$ ǃR bRbR""@ C"oN!m@wp`ExO@ 胮  vH[<`"7M{, %wJpErDvX !P,1HT!UP(8ӿXX;]yXԿ(~I?HWUh:p+Sx30/‘3fbpH & _H!18B"ނczQ3T<8omX48uE] PI jE0; ;aDW_P 1 $rE($FF7K D_P:D_x:/HR}]TnĤDJ:Y.̼2N;Yfh)T3ox]tW! ]h ӣ8Q~]JcnF0C޸en9+oqVS#*9LaP.  yT\UIVgQ݌k1KH 0vǾd̳W;gɒK#vo,3Y]6)_8N n4V6^ jVOUNB/x7+RJ JؐhDl<=%eim΢M2 ~pfm6e7t#t\|Rp 7#s)][0ŝ572GML ]iNZ=1!s}}5;v>FD-sdY4=.J~/[d#y_|庾{!Ac%*p#Q.ptcyJ\bHMBu9i-i%B~\sL H!ҧykKa\ꍜ5W "R Ff'ت“Yg"]XEx3}7DuUIv_ڹ8ϣb:ʩ902}51(1T$):ؽpfPP4%x|j`N,yray F$[.2mXn) 7?<󽽙$z.iSoEWzJtea!}޳YPa[c|r%;wC(E9*ٯF] yG CsGobnA׋nϾ]|1bW\]*&4w}ly#>&Cpn>%VDwOHޑPceqt ;],T]N'NV 2C߭%~O0;Ãm zk e&—2Osct1?rxx]|xxCp$rJN:-Ƀ( (Ӫ*TT?tabF JhIW>xQ-'ɂ0rEj@W`uPX{z6B9LSf38.R)J\/ZwpLgy]S4UXEh%kY+lć '^mnoWn S<9.jĽ/BۛLHu3~d>ڥIK4iM"eQԂձH|>ҙ>. ?G3\Qw XNn+Q fRǤ)C);d}9Uwa„5uf7;hAFq t73\e_m%m85Qw$w|Zk2ycg;*] l4Uy~Φ Ge#mʸq !acaDZ )3:F$&$F?+qZf. 2lnƟ7\v_~hRGkhE8_>vXVU:F]B#e`zuRmÝQ@bfhv9cMD3a$9XOx#ZoJvm$wa  0ԩՆeO=֚ MNr\=9>%K)T‹_䤞*3]}~ʭ܍S_~\rVs!yJ&G LgU\5v#INSJk ʱ~uQ%4S5Udsڲ!׆;URa]ٮuniOQx:l2-93?Eh6(0Q87[ZCUKuqO5mJy.C4Põ\i/x(o/i?9Q5r;~{#tIzY!/t&4BʈtD`GgkAimAB=Wy|l$Z4|Zޠ3G~ O SΟ"r$-O_L^.^y9rײW>>Ce_x0n>63u\K/ƫ[UL={4LHxipSI|t\`dq:Y. ƒ7u7ofSD!^߯h֧|~-XkLx#.!s ) Wn5Y*u][_CuȻW8wrƘNX)IvW'#g>iK!c}+t^c&R?1O^4gvn.Yܪ2[sEl+3^SkvSD}WIנFxkXЙ;4H%'-Aڔ|LcHZxVBq׆sɾECD;8.H*Ty81++1[Yzhh2x/5 *ҏFn4=5SWU. 5T'[,##O%B2yWtJ/CŇF>%ډE0(T2a-8z"VќJ4F:ãfZ{՚+rh7 l%\3=ȑf1B)Šo5D=F&~>q筻x*K6X6h9Iyfo-LK+ͮ?,K<[O~u*PuaU:@`Rr>> endobj 382 0 obj << /Ascent 694 /CapHeight 686 /Descent -194 /FontName /XYUKJZ+CMBXSL10 /ItalicAngle -9 /StemV 120 /XHeight 444 /FontBBox [-75 -250 1278 750] /Flags 4 /CharSet (/comma/slash/I/L/P/a/b/d/e/g/h/i/l/m/n/o/p/r/t/u/x/y) /FontFile 383 0 R >> endobj 461 0 obj [319 0 0 575 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 436 0 0 692 0 0 0 786 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 559 639 0 639 527 0 575 639 319 0 0 319 958 639 575 639 0 474 0 447 639 0 0 607 607 ] endobj 460 0 obj << /Type /Encoding /Differences [ 0 /.notdef 44/comma 45/.notdef 47/slash 48/.notdef 73/I 74/.notdef 76/L 77/.notdef 80/P 81/.notdef 97/a/b 99/.notdef 100/d/e 102/.notdef 103/g/h/i 106/.notdef 108/l/m/n/o/p 113/.notdef 114/r 115/.notdef 116/t/u 118/.notdef 120/x/y 122/.notdef] >> endobj 76 0 obj << /Length1 2091 /Length2 12930 /Length3 532 /Length 14115 /Filter /FlateDecode >> stream xUX\Ҷk=K޸6N@pwww  ww]λo%k{k7}5Qș$UVe17J932XYL,,b 3qظ,|l/%@din  ,M F@[p#%ك bcP}@MXY&coO2vfɦ. 9MhI 4wa뿓K(NOu#[Kupq @~˜Wel,MDmIN@SeKg 3 3oY\2l?Fvj;f,:,L0<&N'V)tffw_`fBf𲍑o_hjlG0۹ys?2G0;A&@;?:on)__lNS8YE9ظ8%QXx8XqK?989YQxApv3'ؽ۟uNog e3{?5t+l fY'_^{ Fp.WZgt.Aџ4}xbW@߇K=!p؝;7qI!d8gQC,J&p?.wPC^!p &^pc5?f| 3 ^GrQKKK˟> r2}9gPy5!p&&VpѦ _g]Y`3!V!ؓ_6e])|1`W!ؕ_{B+_{B+/r ؕ_v]yw/#7_=}QQ{OF6q J S?F2s>؜y͂_'fw4AX7Jn*ȟ(5oQnE_)s[|(ODم%q{"p k0*y2j@"McǁG'piVAoYa2 )Dk}gzdzp`-; ZciGZB긿U0|} C>|NĽɛ/lY Vi<zuE̔UU3v?n'6Tvz@V}{ >Ǣ L|{Bl-G=N`7(x2tl;> BSH!;GR"PKȵ'dU=6 ]β]/|TPk 1&إߩ918!uaYǯTZOϾH ?m&{n_3pɝ^Mw SRuһn!- HJ[Hm/ɗA82=|Fvax/׺n CǠfȃA=v6(ؙ]]Z P"hzF|B&&cd ,^_yq>;*mmEuA4# C>5ƆA!>Eqi x7TeNDS4 іɘ L՟n+GuGu?B} xTyJ U\U4 Imn~ZՋT-&bn<+QWAhOwARnBl窎H܎i$_FHr6r!g-BpgH(۶ Zr'fy2 Sv`⣼>lQCqtilތ.-%nP͠&h/eԻV0ö2&i$^L}%ڢy-skƙYg(mzq&\j`?/#)'5P!&dK2C% vTTb S_1< yº (83(|0g +6x"`]#~cAq4g݉8k " ˧U-`&ha)aNrqkgH3pRQуK/L5M(M}b"X?JcJ챮靍-@yo⭑Уd]Ev%9iPEеXM: ?՘9d9$[|^5l۽F|93wSX> /P= G\sęs |}ѻ4m6KۥζA ϒ;ZZ3`cn|T,F:H,|GqBF:ȤF5,IKB۞ͦ; j#d>p>Majv/mgn5m \Cuֶs"Dj#AY1;B(ķ$B̹YtvEd=}eGpD91ÒK"8P| "R8厧@Ѷ?kqd.Hq2\V"eBo\Bqg_~SdNJRD2+M:{6d_?~Ǚm8[m <,lmur1=MVXwvQ Ddk0A!02s0$t[y9Wfl6XfZDqgPanrWQ殫2AO.uEI3'xXr"%}~m1'0Mi^_ C*/0.f6:G)w󭍵;cܔGy}VS̝c:D##%e $Ч70woiyvmr<cl s$@k.i 'zvq?ؾ%,<8˽ NT Rfj'jp0,_4DYKJ]ofHkQC?pޡ˰{G:Ez#}T,H uc;9 ͗4reMߜ*T͌^4 TW<\47 !KPW_E41Ӣ$MTSi篲R˦Pz0µݗu{fT5 !2~} ֡9EZPruS1EjUN{zi`f"^(jvX+%vVT8"VeYfU+'P3VY-̲8Um;jcGbR*x"]J ~w$:jIc'E}Ae{13$qvWDmbФ@wɗ˳q<,# v1O)xxA Ozl3Ӗ BkD=HWw/JΌn5 $#𐟏/|zS"ryC@.3 Q\?AgFvj/Ovfisr*R H=ȥqd>( H7&Y3?sz4Ro?i&良Mٚ!s!C*;CgiܟV&*:!BD2eRJNQD!:^ ]iKv%+Tayr*ޤ~3 1^l7B_of*%ﶥ'EվdjaXv6ku4Z$>=IJS[ްAD%T(Tӳ %'B|g9Ij7Z#/T+leRv7A( R5Oh7#GdU(3A#9M^?狉{@۲' @. VI>̫&#.PΈpAtXY'T-V4L>cL {]vGN}Iխ<-d翦+l]SjVgI83Ւ[l"xo!^59rKs;q`4)&QO" _YA ψUB+Iސlf7{_:RQwj͚!ٌk5M|K1DyheH q% 5va04]Pd(QH T{׀WAq[OKá}*^仼P@J#FQH,NzͲEI]9`ݝxYmUFQ67o?Lɫ`ԏq`!OEgnx?wjs}_j_b#Y‚T,CH_VccTg-+.8k)dNmt͓ΘKۏx&RQ{v?-[Pue6: m5H `2"sYJUTP DXR] +]^1[j<ۚ+Npib*sW!4 ꖓB?o}VoNŞUTv%.t/Vɬ4C  p078O%E-T&^nvo? [.[l/k:"~ OO"鮁jD{jjR]&cZLyzgo,de:4þOҚ~N`aDΑ[Tnɭ )ILNz# Xx:Wo"3kO:FW~k$7zpzI9]Փu$EV>H_0ۺ:XԬfRB>`jn)FVd`x0#=%z,m% $[$h0,hC$~ƙ+RS׮!;JzZv$]7=Bz!2o>q.z:ԲI/Gpwɏ)rZ.AkD\rZU3(U@,7,X50(,@i{S-A؋\TGA -z42Ԏ|Tf5ȳ.EN=-C] >+V> ά|ƒXkGY Bx-b \陃@h`c1VΥ =O,Q ƼM`ޭGFŅե=c6OnR q!4ƿ Cl |iiRs_3oڻ:7/WB\~Y e07}6[Izb,uK>4/"'WX TT85-l=0 {v|4"jiW._)%|0+[7 ']:TC=c18TیXՕOFa7vmscxF]d= dQ9:v=8YBD}I'{V >3<0~X=x/8@醫o 7~c3V}쎻:m8H@TJ3-jޏcG![=Uߧv=kmzNit3|_]#rUfٴ-;/%)N1Jp}Mʤ[O_pocXnB1pYЬM^O~aS/_GkϛL$ #X/C]x9|L}-S+pGrbֻ1uzs"pt~] 0]ZhN+/z-R!HY/1h*%>3䛳̕F $*̽rjiݢ8lh*Pۓ7}!vdεNB;c^N14T6(Z_B{ P2gIb/e(̖M D$5i^spTzAbTw Λa,hynHkz3A1=j. =a;!jdmP!* 5h]ǰA+vț̹Hﰙ֟ya*' JpF`Gzx^j"7֣LCns+՘]{**g ?D61Í#}쫳ٞVt[4-\C^:Ѥ0}GI?jn2}+͸0'D9<\KT@R?ɴRd\@ipMaSI"?Q8%*Mp,#JĊ\XϳbiY5~ӻ3ߞHggHn06G>]#1C>2W\4('˚߹~c0#XS.s0bJQ$m4PT!o4JO9]"Xk5SR ; ,!WF]dKMh8K f.I)%F5sѷ~waPa=?c1#DM`׌erzfZy)f"EϬ kEQ^>@ ַ]q ÚNoIK6#,`wQg"I˄uZ"N.pHā-I:mW9XESM:N~Qn"WY?1T/i ThQc+ wS qyP*@l~WxGuTIMfHdU~I"SPk>O;ʰj~}R}\ ]kفal>uGD}BXTveqteG+ oCaײpi ^lp?pOnd` I*BfbtmwtzǷY3*ʧ u8`G.76]4IөTH5=9$ft,Cձt:?l=U)S%Z'➉-ybOq2i?׿\ewCNkBW9曅UJ~yΎXзQdpCN0BzJ븜qL%IJl+TNYܤtY>z #f&)l,l?iqqĞiYwc]679V{gOA N1~{ts>[EzF9jSeL i' $M Z (iQͱ[5ЯABAŒrvy=omA6 HyKݧWB?vdFJzevZ{<)!FStdJ?_׊Dq<<'#˴} `撷Ahn{Ưҵ sԴsmޟdl~0q8ɦ+$P3:ꡒB{ʀ>Lo_!f/fWuTV3!BʨJX|[=4넆)ܲ3TLMLM?+g]יO`%7'D  }~eE`J$+@<ѽ1 v@Rw!Ѥwܾ> M" .I) ^ ԟ4HV d CfopC_?9LK`:犠͂X5nVM$)eYt&X)ʓ5?]ʹg lՌmW9 5]JOWx;w_Oq7Y(ٸi}L3z[:XLEXa{l/OH-=ts>]s RcÎ(pĴ—"/9pFzabMgj3m_||!ɢ^ AZӘ;z4)d?-ݹTAe /-op(%]f>*1$P%sL5tLQAJ}-";YӾWT>-ocb "'BG(|/-t+6a ՆJ(COQZ1,xaڼg$dzy<nгDŽoi i]&L2 ]<:8Cvsb3yN.ialr>7GsPiοs2E>)uFݬ7wDmZ֊.ǧ;OmFY!zșWӚs~qi8RHzR6NqDfF(9"`#F+t! Ϫtl/dA0&wHf OC6ǖH( \r! IZ1MJwf_e w4U5R .h'dX~y%6s;>O OA&F2>$sC=ɂu>vf$Mw,yħhmdrWEW&OͰ:I٬^]kArcuVDh+7/nlhavH]R-\` l9rWi 6lWq!T_Lϔ48_,p?P^Ͷrn,V+<,!zڋaC%qAsQ[ۘ.j/"n5B=o6Y ?%FV,?' aQ)EޕAǤ1Hh)*ϳ2 C[FuzT (qmk#iĶY0:}ey+2n<S9kfQwBm7(=x(/ *5̬Ҽ9}jnB)NI6[$!lfF A|]e?2* Tqm^ҍ)IX Zj@4= s@d1L0=}Kb퀳v{f/ZgY:7<;e/M8QFC鏸zPO;:NbFq{9[= b~m_|eO'5 3M,[ &L3ŧG{2 B S8CEHuW[e5C].#l:7ֹImT0w{;޻$NM:wZG= 6h]vTo=UkA4~m,<~B05[ηX-`l Gku<$Vܟv*>Rm`YwY|IBdPّbyvLx$LchF$}U_F.?y|GI C޿4Qп)Lw `f5"qLIP߲Vkk2qeƏt1}{'YSg Pt̲2XwJ7>SG?RW7)-Y&p g}lk#ɞPۼM'4y"P8R vq~8󦪵ξXje1FOL]PKtWh7U,QdSX)٨+| ۾[c]ϫ4[.ҿ2SA#rvSN:59e+D[ɚ"OXeAh֙uĖP#}jA8m❆jQrL_fÍշm RklMcs#:|>bSJ[F ijd#ڀ*ҸIqLJX 6 ISŽRgj=1_Vf:Z@|^Y^B5Vq\z@a."&gD݌a~7_ފ2M>7%7҄%Lc(ܠht`s14z=›sg9tg]lh5;'?ٓ|}5mB0 D=A:6I䙻#LU4<+6elќ6 R@ՒbAE7cwKz,|EF8 +5I;c15D{Kם](@ 3eY;Furg]usL0A!qk_yG@bIWj3kzFDR#2 {ة&e>;gi m,^6䩞ޣr=bvVVM03D8KYƻ tqḹTh}Z!@bl\7V5!tOLrY?U)*..=?fZr!iM Y5(0Yvh6y̜6bdWI": tSbv-йޟ;j"h%ጉLz%>\">a/|(9+{Wt@1vw#Uƞ"r7M:[Ő"8O&6@#<endstream endobj 77 0 obj << /Type /Font /Subtype /Type1 /Encoding 462 0 R /FirstChar 33 /LastChar 126 /Widths 463 0 R /BaseFont /DZQZDP+CMTT10 /FontDescriptor 75 0 R >> endobj 75 0 obj << /Ascent 611 /CapHeight 611 /Descent -222 /FontName /DZQZDP+CMTT10 /ItalicAngle 0 /StemV 69 /XHeight 431 /FontBBox [-4 -235 731 800] /Flags 4 /CharSet (/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/colon/semicolon/less/equal/greater/question/A/B/C/D/E/F/I/M/N/O/P/Q/R/S/T/V/bracketleft/backslash/bracketright/asciicircum/underscore/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/bar/asciitilde) /FontFile 76 0 R >> endobj 463 0 obj [525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 0 525 525 525 525 525 525 0 525 525 525 525 525 525 0 0 525 0 0 0 525 525 525 525 525 525 525 525 0 525 0 0 0 0 525 525 525 525 525 0 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 0 525 0 525 ] endobj 462 0 obj << /Type /Encoding /Differences [ 0 /.notdef 33/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight 57/.notdef 58/colon/semicolon/less/equal/greater/question 64/.notdef 65/A/B/C/D/E/F 71/.notdef 73/I 74/.notdef 77/M/N/O/P/Q/R/S/T 85/.notdef 86/V 87/.notdef 91/bracketleft/backslash/bracketright/asciicircum/underscore 96/.notdef 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 123/.notdef 124/bar 125/.notdef 126/asciitilde 127/.notdef] >> endobj 62 0 obj << /Length1 1501 /Length2 9749 /Length3 532 /Length 10655 /Filter /FlateDecode >> stream xUT\֦q`Bp ww p(p(+! OH@=-F %f&%^1C=bvvp]n`sv,`nm-r$`W.pSFI&ܢ9`*CZ`ӥmf@]`g:26vQ9&_akik5 `ab'v0O#c+ -?WAUk#'ax= x"!&`1vo ^'| X;=`c  7`qע8@ A.>lv Nx7i@3ɟhhv5O.bgb'z!pE/);ȟp)?/)೥M|2 Cp^S(!ʿ UCp?o7O+cgoqB;B_״ ፳ lMpv!܆h_ }p 6B nmpn!܆_@n/3*.fqp1MrvrIx888򯨙3s55=fXs3`E0bT&ԯC!BB ?5Tg!6%IʀY~\b/if>u= ߙtl`9\qkGP@)~8 an%a~nQLk=BHߥ]=Αw-. D~VBJ`XR&05Mb-M!K$e}9/ǘ쏬RҤ]4dTBOv|FY:]wWnu%nĎ:D,7Bʼnߪ>nK4 E6mɷZG0 @)R>\>u=i` yh6X>eV萱9#r:W= nDn\Տwz[A eIU|Wc8Dv%GO0WI}žF#Y]1VOnoٷMSt~CSجp*z=_B'8#򹈧*<Ȭ6(Iqŷڷ0H0$]XEPxΊm?W0g4_܀n906B)ʷb5ᄥpmG!Q}t1;HA`dML]hK/I=*|r|hf2nrK3uh9NMq;u| (eY=rv0=T%ObVz̭JBE7pADӽ,{g,_ï~d8AIJ ۴}DT.me2- ob_ f%XJ7d铌lI?otoEQ&V)qx2>b$]ihzekҰnj+q=8#wk.+ ϟBCOZ__=Ec|2P߽7yݥL͘ SDikOC[w u0^d<ԟ@^W[5r'!0dVt҃d@L݇W҂ 9%W,~HnrsIlrYQM%Ȫ{`苪Ě{WXs[G !$&,~zOE" gxdOʛȊvݯ*dI@' |ĸQK[LXTƢ{^>t!T{c+H$z,Ha{(wc8PGY}le80 @Kb܄mę P\Zxd-2~E6FbkJUkGPEћy6 H\@YY#oK󇍠{VZ0P3<:frXXgM\YՏQLG_ƵRY0x _^8_n"&y\_>.ژ6r5h )'6=/ lԅ r}/3ߤZ{vq1c^}>MSSW+d5E;,+nB3PPyxEu @1xz$֢ 4iI £3tL%ajc9b`/&08usv7D.;7=اv!\\ӏZSk2&igķJ~]Dwɂa=Ķh&H(ݓ b߾;_Re@{L("eo^ʸ0rҴSln;)#w)=dz*|kG cpQg v3|D|6(|ء)AkTZ2>4w5'ݤU]Rr|^IX:MB 2Ŕڏ,`B+VYdl\I\()J 5"] sN )-?ꬤ EӥzZ*2򞻵#zC9>#2KukݶmbvUd(hxAwW*RmʭX)M),w €EUÑ-9n>)edK}x%_w/ѭ 8$j`_|l1htAjTa߹k̶eFs>;ZI5S 'AB{#'o[sV*]>vK\ p)`pvi}w@@ڦ˻UnH[}kp ̄)˺0XB L?v>U`z~+@H+%ȯqmuPo$  ^Cj٠mퟢoIncCcS/Lܔ5MP#8rQ:R|?IN؇lKx`BEem{э &T^uBw+FywTW\=gԕf@ժgj),/.iUǏml`q7 8O:9~P?9=’Xjf@T*f⑵0@Z$_Lݡs<0Ъ%!huע&gq~-^[Naak@$|vKU[1Iāh%qW?7ӓ3"X7~Єpׄ`.C !TjFG 6KO\z{rxW*iBQ[\. c[8ʞܘ_JRT;2lu3*1kɉClK3)۝c@T,X3zzj&OW33 V18~js,ȴ ßGLW]B>h.TЩzKihnB|43%&[ߩ=&G׵hOf]QLc.lGR!g:)KKاl} FǬkSSC @`8=a{l[\@hH;O$z$T>Y3{, JJf3]v!^\Áq&~glmDLʥХbү㠘Gڄ-se8 Vqk7*}?R 5!E\c6XK+gH_Q9&m a;M_#L4Fn(g7 IcD*T})`|%=vLb0el$R\>03uȸr茈l_.cd `u=ri<+n3yjKD2|BJx8)k ;[UHjrblsP: ݕ֪6ѐ4[=aUtWʄ~2.9roaT_&,6-@\Ր#uW_+m{ xאTEw?``Z8uuW$p!/0L"l~"jc/P& >&ET8ZB?/c0)\rBCXԻǁNa?*7mgrrN6($IXyӜzȲ.T!$ZԿZ!RWwnڙjV4-J/8j{䯦 tJ̐0HqQ,p$Cw\MA2Fz1\yaj#O*_لXgi7! K3rqMUڇk*~YW0, 6]DIϣdXT-\MNA?*N|R͐Aq\:z Tn PȞa_'YŔl.2}̂6,-[d!i3BCGqaU_T(pYs nBVVn^ىPyW.%<.pWsbնo4| [dyZ;6Pꖪy'63PXZ%ʅO2ސLM3mz'.Wa+-5Fnsx>2Y,VÐOK!9ТԈc9V]{QK^*Mv+((r ^ujB/Fnq߸,(AZ2i޲Uj??to*)<=4Jg⊰d"Ұ%cчv!ѩe TI*=\unw\`R!Dzs1L`.J=UhT->w ~YS#rwd+#a*/M*2}A Tʗ1MfۻibGĚd.ZR0<T`=v{ڌSD,x%iS7Rdq+_@[c=C^A*q"ifS qI)}CirP^p꧎&3*af zR}QG4mz26xkw2!IUcNHN]P3[l薯a1k?hg=׼jZͬ¿gJfS$n$)eqm-kSƝ\8= OL?+ʆKCQ~5*)oVcᄆ]-qYͪqn%^Y_L:( L'Rnf xt5šf }EəvÕr!w#{){ݟ[v3*{i+~9O Ubin ! !~Q?#/)uO]_뎫OV`+'I ōPC00;}wsQUxnռ`i,lqG儯NʓɅu5:&K!/v݊wu;@OqB?:LmΖ#NLk3vG/+aI-i)U?XIq{h5=x =:(D-i8K gA'{lhJ|bsǪEΣƇ%LKE>uxzӡ `}O4:u˾L4IHZ,Q;(f\mζX^4.ZO͎{G;v_Eб?Wr uK bOݔ! NG!4r3XnA30-v%oz8UVu"⬬&XWSW{q(lfn[O҇L4&*ڙAɉO93ֹiog*P5+R{GH } $0j/M9r yvVq ^[2x(M'w`?x<2Xor'Up2rsGL/ԾيLX(kQ&ñQB 7lrI*j L\EE561(@.t/9mwg_zFIo# x!uQym3;L]k#TS) ,j>LY0#ک|*FcQ|OAgc A2Kb^*-S6.wbJBQoI[ 8i=_>}.gUca$Mk!a?boQOi60̋{[2n?t#q4=}nZ>ܬ4 ) 8RaዟhW٫29c 6E)OD^Zŕc'*@ѽWDMkA#@ cY2$@ oZc:8TU@oS,ȏ@*MqNMX> g2ǽ^dco>si>;X|O0Sx~pTmŞNQKN$ϝMmNy%SF@,7 g$Z%jJF;{jt v(" Ƽ>ih-enHO' B= )%0﹐aN+0@䁜:'ySnEӀ]d3NN}k_ɔ(2judi!BL̚%vD ,Pֽ Y|pB4[CdPA%K Agsw|N~6g@mפ y/p~\{>|+= u}9a^C4 7 +=E EfL >[Qȷ@uXIĹ8CIR5/7om:{"+׸ɞCkLH7Q0f>Viߙyنr]U2>9Yx6wTr.#(3A"g|Zobfcp#7?y.ʷB2p! ِXBeo0Ee7avzK]ÔahXՅ{e BS SgCiY:BE2QopS_gDNJ#!ӘmrPR$ybUSAtʘXM3^WC/?6wMjD(/[Gh%AA麑]sxJ;-%_gm ~IT;PY߇PhG-C_=c~ F1ÄXerk1L%B%wR`6c%x`J\&QW%Gqi hQr7 ?xr5[O->Tɪ Hۣ_bgQ,#w"U_ꂳr^@Jo$H&~M:84z4Lh$!B/;le˦Hq|9¥,}[A!A۬}/a8+M=, b.)Hix C̿(0 QLn.WW`6 XNok/v-{AQrbtid&.փ4މR0[2]SoA+i׺B[袎2i+ƴD6&zEt.G$@OT{4R?y'wnE:K:Fnoyw"ʯZ$*Qi˴Ȳ,CtFlG'۬Cynf'q,0̕.M`R0Bs QǛ> "$)UyoWIv@ўS!r^BOq1[c>lOsSkHՋg>} Rt@TJ-^itĹ._ʸGYqNm|nںŤt=^]NT&% "s x٠KZu^IP(ܝdNn2=6]U la闕K|:_Wg?|zR94U4=fT*P!?OgHleϝva!o㸸LA4*oBU=fіֵOO> endobj 61 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /XITKFF+CMSL10 /ItalicAngle -9 /StemV 79 /XHeight 431 /FontBBox [-62 -250 1123 750] /Flags 4 /CharSet (/fi/percent/quoteright/comma/hyphen/period/slash/zero/one/colon/A/C/E/F/G/I/L/M/N/O/P/R/S/T/X/a/b/c/d/e/g/h/i/j/k/l/m/n/o/p/r/s/t/u/v/w/x/y/z) /FontFile 62 0 R >> endobj 465 0 obj [556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 833 0 278 0 0 0 0 278 333 278 500 500 500 0 0 0 0 0 0 0 0 278 0 0 0 0 0 0 750 0 722 0 681 653 785 0 361 0 0 625 917 750 778 681 0 736 556 722 0 0 0 750 0 0 0 0 0 0 0 0 500 556 444 556 444 0 500 556 278 306 528 278 833 556 500 556 0 392 394 389 556 528 722 528 528 444 ] endobj 464 0 obj << /Type /Encoding /Differences [ 0 /.notdef 12/fi 13/.notdef 37/percent 38/.notdef 39/quoteright 40/.notdef 44/comma/hyphen/period/slash/zero/one 50/.notdef 58/colon 59/.notdef 65/A 66/.notdef 67/C 68/.notdef 69/E/F/G 72/.notdef 73/I 74/.notdef 76/L/M/N/O/P 81/.notdef 82/R/S/T 85/.notdef 88/X 89/.notdef 97/a/b/c/d/e 102/.notdef 103/g/h/i/j/k/l/m/n/o/p 113/.notdef 114/r/s/t/u/v/w/x/y/z 123/.notdef] >> endobj 25 0 obj << /Length1 1075 /Length2 5212 /Length3 532 /Length 5938 /Filter /FlateDecode >> stream xW\S]%JMD:H$ЋiRKG@@@tttE{=s%%DZ(&!4* 30h7O P J"˽(7mɫZ Du1I ˳THOiTR{BlQUoo'h21ySg`aa$ˉ{4 _~G|kZ/cFFĭq‘Q ϶2y@a`䋾#P]r9Ia4JĪ=-1ƻ IҍAZ7Kɛwr[۳<[|bh*ڿt搨juOȼ87 -|#.zhk:u+KX'teN>o{I!n }3=>9,kiI M3}c}(}N)_h DIa̳g+f7ELK'r _NiKP&gW\  eIaV-Dj<_V-:Ӄ3|ȥour_eh  p;\Vqz 2S:bKܧ|CV32w+*axj8Wv;2u"> IvN@evذ|kÓ=PTnN,?$B>r9܍vcj!wڿi=?Ja#QLDv.N*Nr,L1fa>,%сmAJۦIʦ|(J_ ;ުksꝮTRQDw[ZRȓW3jZSg#DL{7ҮVM:r'-Z^5ĤF+Rh<,>tH+BRMvdP0({xwbEjPG x]O%]:Q܏=ZACX+=̤K!뒡Kf~94ɕ .w8\~-ȸTiUtmTA_}UP?`Wl\![(욡0(zزJo-{ASND0q 479эv0z!qsl[ x"xz\ep^Pcd(1(4Z퀟 ~$w"ZBd}Ļ 7j#YsR2cnO|BQ5X]5CZ cٜy qjM`VIzݙ9uXZ; 29]~+蜭8)׹ՙ}*R'N`Tb3)8fSSh kH*Y - ߺlj^UU_ b7mn3a+hx{k4]qJ!ͺf֛dSnDyhO µkxC&as&ASo >[y]@WRL{zgQzw1ݠr#4byED"fG|CR|*ӂ]܊N$kX·vաt&Q ʲ·ęgnɑR\ pt)Y4`"w՟x1hd.yԶD:%R>>3"f^x兹l\jJid@7=SuɃyƄ@Wb Wl !wsM5Pc_K%-&4J45vlB}%^O@5d iHmˆ_Z_AEy4yLtUuc͏7;YCkEf e0:N,Բ#3) :vk}A9ܗU!K.vH?L RE)HO}[Ν!;FjV)@x9w;V?CB5XK½Kr(Gd5."RDY2<^[؞dZiZY * ۓ׈?mKy#eƅА[ChX#&OBH4w,e?_mX'[qdFE+h-2\憏<}}aτE]_HC7[F˻?r,Fڽe9lz|o[\. -vgRztJ>T )U8q ֩?( }mCت+ѓdyAl5ed泼1 ^hJʑ6;If#T9[Xh?K(?f@*_bDnGh*'.߾-2hj׈T.զ]3^;iQ^E2x;1܌2NfXf1ϧf B^j vSxL݁rl$8XAΧAiEEiՏ7s;,B'fIengб+pBHwcpġTTlEf{uDS3AFS<ʫVAUϿZ$+;f:T=niܧRBpFU'd9pinmOE B0ڽ7Ѿ՛^/V5 Cs~?a^lNwWjugA0CxvΘ?z_UryiM{E8b^hR[bqQt0ZPkdZjXy&@*Ҿ-;|4llgypk~Fcۀ"١IiY1'(g9FE!Oii 4TB~9*2ѵi>Zp" zTXM)1%U_ %HFYv6{} (xXmNݚ^&-[%*$3j{,Bp2ɌOZ.˔P<7LO@So9Bl5-GMe3F V3m)=C,zs>L/Ap _- '&yϱ̖j`#\.0!pq|1ÐTٖhZ.b9MR!QɽЉAI}_\JOo\I(9n&Q7D\⡨ U=hjv9`snaC<#p"rbL 옙!WXyr!-:yX/L>9y/\(qˆڹ9db\Δ=s6KB3aO~U^+j|F"Zg% \hC(6{m@I7Qf X IOҌ"J޾c0s-Z>k}bLŶ^)BA_piIˮ *rU>x]9ؿ}E1R ""2+RjhfiqF`5;c15+I ,Y}omp1֮vPeӔ<-"v#OZ.eB[ˏRY ŧ[5zf]c;TfO}šT2JŚq.|(.4H{3c> fkov˯zC ~ѼܵPdn#^~^ct LG#K+bﭏkQ,z[߇|4=E=p 6T$c)>5Uo)dV{4q~~bL@0dY5uE?VH){h~HJ2yG1Ev~oK|hPUoҠqќ}5 `3z$s[mwgDO0rw^3^0q Wv_KrClޓC h\HbxWźAxWwa]Nl+endstream endobj 26 0 obj << /Type /Font /Subtype /Type1 /Encoding 466 0 R /FirstChar 13 /LastChar 122 /Widths 467 0 R /BaseFont /UGUCLL+CMTI10 /FontDescriptor 24 0 R >> endobj 24 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /UGUCLL+CMTI10 /ItalicAngle -14 /StemV 68 /XHeight 431 /FontBBox [-163 -250 1146 969] /Flags 4 /CharSet (/fl/comma/period/A/C/a/c/d/e/h/i/k/l/n/o/r/s/t/w/y/z) /FontFile 25 0 R >> endobj 467 0 obj [588 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 307 0 307 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 743 0 716 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 511 0 460 511 460 0 0 511 307 0 460 256 0 562 511 0 0 422 409 332 0 0 664 0 486 409 ] endobj 466 0 obj << /Type /Encoding /Differences [ 0 /.notdef 13/fl 14/.notdef 44/comma 45/.notdef 46/period 47/.notdef 65/A 66/.notdef 67/C 68/.notdef 97/a 98/.notdef 99/c/d/e 102/.notdef 104/h/i 106/.notdef 107/k/l 109/.notdef 110/n/o 112/.notdef 114/r/s/t 117/.notdef 119/w 120/.notdef 121/y/z 123/.notdef] >> endobj 19 0 obj << /Length1 775 /Length2 987 /Length3 532 /Length 1543 /Filter /FlateDecode >> stream xR}\LY^[iVŞ&b2))Ҋ۽g̽u>F{$◯ QkPRh[~˟oy}1l RPxyy1(q]]#6 ]_,:K& EFi(<,\ {9C)Eá@% Q5TyOE HAj p$#$بWqEHG@KHBT0I>%*U{>C *\y!UQjH?)4$5%zaJ|!'1R<b  D{>A׫+ϕ{pkN5Q>{cǘ@z8- %13@( 0/DG"8h\A@;$虫pXOS<=ɄD"X Hw\@⾗ um+pY 0Ԓ[jĦ´Kg캚1V7D{Lifu* -x-r1MǒK\m%;@C}Yul*sS膨"{q%}lUov\\u`c=`,* k~74.gl~k)s=uuk@kjK;qE׶o%r\1qOZXxϺ֣ȵTʴĩ6ƒusxѩ`ؑ ùpԭy68[-*\`6^۰uOjR[IvǐLD$.yYy*5եhƙsѵ>g2ӣ [Rv(FGzGٶ~)gq |e ``Ҝ.P5$BT=Bs=F9ז\̳X<_}fЍacF6o0m-Ӈ0uR.\]vEZo+_vϬrߘXZ> endobj 18 0 obj << /Ascent 694 /CapHeight 686 /Descent -194 /FontName /RRURCE+CMBXTI10 /ItalicAngle -14 /StemV 107 /XHeight 444 /FontBBox [-29 -250 1274 754] /Flags 4 /CharSet (/period) /FontFile 19 0 R >> endobj 469 0 obj [356 ] endobj 468 0 obj << /Type /Encoding /Differences [ 0 /.notdef 46/period 47/.notdef] >> endobj 8 0 obj << /Length1 1882 /Length2 11325 /Length3 532 /Length 12377 /Filter /FlateDecode >> stream xUX\]w"[அS; \A!wz>}3kւZCMl ٸع2\.vNT:: p RnN00/*@s00H [Yv@gh+ 'rr7[ fGX[, T)l[{%O j 5Z|@T50T u\Inl+.@@l R=;/%w '{+)!{7y{o 8d?-@/ZRz ,aarqwV0;{o';''t!￾19>~b ( k7 uCo@[CPNs+ E?E# _a^NrX ;;[8|\쀠?!~POD  ࿙u>#w; S_+vݠ[ouzv+jdjvjaRTA!h6?-Z$-YASCڔ0?UWCP7jok!?UCPu?UCPu?U7 A  +G=!AO ?'-AK3P% *hB LqBs B)rA]B+_uB]PWI B+/Ѐ |B+>&~lЧtB AG3/ٻzd4'tZy@g~G=VKV"r%3ṴҶmIjzaU\כ0b5'OC\2<};/U}~ ;jvV}v}2͠t$QW0F9N:QM;I_nqM>xf}4Z2rpnU\-^J!FY>Z1>hmPKwr;h_9w}Lt.z#z&erOAYnIe6د5hD),x$6/fW*}&c"J.[ )^Ye!ͅMe;9~Mz/>*L`qxn2cZ}ؘl\6%# ѹ ߜU𹗮CAҞ%%[HT?Gy*XIP͕oM֡$S9=t׵Y))\۫oA 9B/jj9#LlEbB w0ڎ08F>,=*?}YwR?)C6+"{i+Tҙ? e{Вr9'l3`Opcvm Qū7΃[G&"EClߎ4Ǟ)HvV>|7?1VFPj%P8oC~qڔM̓Ɛd7D P .|{uy./sKx2Ny$4a(N[zՄڦak}~;c gg+fqQL0ƢBea>1&V#lٜ arEwiZɄ5yo 32i^zV"(Pd{O\;:L%t2qh@+N6<6c!@Oa!KQ!5;< ña``~Rv(|&mPq~gQ4Es[|J&D G_?F٧\rCSB/˼S$ ]roNΈH-V~7F" e{1S%}-W! 8C>"a8jK~6LP{zƨ!@SЄwb%zw&ƨy[IOB^<;oe!PFG74[M;:1I [C)3=Zn6{Xĕƻ#>R>G cGO\95pYQ+J :v\ԚrIcTp1$a5Tʄ&7^vMnWjAqvh*u_ʩ0^d(vAţ[i?.Iq7 gٕ.g|ʥ:W~H.'.IlpgwA 5XQ)#m?h6(S@mr:0*~MDMp660XWZ1U+LDa[KDL2/ϨU}LIm9l?eqxZ ,V $zI3eSIJEF1|C^ap_>=s"f4FJaw&*@#_4w}lK;f7"o>YVO=3َ-Is >ԊDŽ#&nXLWLUIR-12D@#ۣL_i(*hYpkjҩm2F/ xmVQ[‰LznYq?TJ`ҕ-NHB->Y` ^j^IgM6GvM;g`mFEi'*+npKa7V t Ô(sK33Uq0bqJ P1 0TQ_]+:t}||Ǹ@C;SxK.QO}䒟~pJAyW}9݊N bx39^U+- 5,#21AXPfs"( ,ᨪi%`1|"z"5?gw-ը,,[>;M'j#FK.I593jmstņv&M ,J-4d. 0#,jPyQhj+Uiq.:0xGe&aŒH hg5yZZt4'85vDBϙ)T D<=?֒@r|=ыs5"DJ#'0 ppdJHئfMU8EKaT2M?rxUhlLE6HA8 CòwlGfX)ҏi^1ޞfQ g!g7lr[^lA/O pקt.(gbBvՑ|-B:vgٷ*mO<˝sMxɮ~Nhf*0G¢`}踃i6cM7bS>JVL;\:q2sW2G+YEC_r8#g=16s|/HD@md|ȊzN>6}hϳƃ 4JSeyR4oyfUE8eKs>!H%߷,+!8\[I +`=w|_E.7V^:QFrb`PZ@RW)ڙ8|-qP0eLiCRFkniJA/;F<< |R A'P\8c~٫n\7urUJCKz璘dvYV4GrOYa&EoƫX_\kKd r HѰ OtyZOb=DVS^ .UݻY"qgr=<2 >SZ}37NJqT&=J~6si8 "xy^s&1Tģ>&!U]+CIC.7E\k$ϛegZ_grZwa8dnXW FљItDъz &h"fXI?FJU;{@Uv$֙w uxi_!@bޡ |][; +bd @So" p#MGѮ<VF}Nx=Do[^Vl3̑zx3ЅrRSPkF{@eJl[#`,lxŚ2@m&qCps\1)$d d5@ Ez2w ZI]KrMco$GPUSgP֢85B*lT=XO般jr~G@NK|.aLUw ҲMnb95j+qyo?&&/ %Q E6&i'#ߨ,CLI\Le0]1O1ny?8UMɴQꖣt}T2.k=;0k W]X!iL]Iݮ>4Bo@;mr)7}nu4tr9GI3G4oDfy \֣hL/M;DDQ|UrAHpwq~TLuҷ*ՆƹysYg,@ 4115" ھ bmz#}2ODfh5#r`paT,5ɞ cna>dMer(>"[W/c߼ ү>:9XqG5B'ꪣjSޥjֵ>.u`CU0bQnا=ʺ>1ܻ?2[3]"mV,^ 9BBƂD.B(W-}SY555MSyURxK$h鞅#˳|:ykWD!%Ԙ 'D7YknI2U9aT{yhR%!}pF;_n\dV;VuD`F/#e(l-{Zˋ=O*^a4cCK\FՈnT"Ofτ rVaz򯮨~hWe}?zc_<*[ڢCjNŽ[Df_ZeO)Ћ5 Ii4=vukDEHpsy=x0ӳZԊu6KO`A 2ORy0ӟ{{)C t-iZL4_UмH?:%u6oWC0so~dž|Y;8cJǹ&C5t˩z睍ڊY>YOY"XN})p B]je w0A[US,-I!TMZ 9889[q8NղBJY41s2R{(zfui%u`" ?N\ΓcWkޕJP3/E4чkWn^/M_!L$m~1pLBaƣW@zÒB9]ʅLْxvҜO{~Z'놪Ͼ`\'pԣ!P,mፇo`ʠlP)Ơֵ"a1(yӒM7ό+?rPG@';W_۳];+r5THK^1c;d쫔Y1ֺK/ pt&r`(}VX4"7jQcYKR>Nh13,)ܥOln旈S}>^ F;}LeOV珻\AB`g|y#ZWp%%(MՂE}dT=jP@@0bۅN] 0wg>6L@]H. :뚺 Qu%eX5j3[\1!.$޵hO/ͼg;:C:jOsV@ ?B;Qw {gq/,lA'+|?wo[k->7MVnhrv+گ3NM>NQm-#k6)8}\KIiyҙ׉!{nשiuMzÖa. "'{xZؿ5c㭻/7C_?7Ǽ'J oHcv6.P5ӏFPȊǚ]FqaGkZpםk9ܻ߁`i=Q/w"N`imf9jYS]e 9a^E7:~Ci`Kī11N1W ȔrFk%k y^4"!$EAhpL. q@]Orum%m~qixc"u1=4MsO%<`ZNjm =⠚=' QP#QNuB4KpDId'^`%LrG_7'e̯%+]V 8007vB ="la/ 9Z~t7yTf[[Y.&[Tkz13CQ8ox^U>_"|gH#Cw6Hםfz!3QRu0\8*ϗܡ,V[E@ʰsɕ jԹJyzDJ+2f8ͲydV?M0o9e:+\ D&$دCrȿZQ9Z߫Sw4tOm- R aWVP;~]D7۟qM%r?_RDŋe3"=LEwpyĞ p1E'"CP1cRQzm|Y*,xi(gReM/&A/+IAU`? =D(~U㼳 Hy¢nSf@>'ߴW9j:+&/%Vp *z}ϭC" G(`+^by[L͜}1`5FN%L s̗#(˨Y"4+^ZՆ'z\e7rwEJ\극_1K[+{ޔ|5e-xmːϔX )h|iΪ##$T>:b:|"`>2QQ25Iܾҋ((BEC c$[''ҜXI$;DUʍ;F}WXaY@:jo?@;t̆yHa' &Ѻ FG4֔Z<\+Lbu.Xj|(XFy}qY\=S#A9kp`zct' ҤVxbSv>ɵ+N@ϊ:6Ŭ; ?v7\ uqQa Y춙 $U7 0߹UrXk`"nq[e72~XU=qFzO bmåtL yׇDffwb&ʌ}y%+ti-']{f@Y IdmG(=9CDHڒ!6Gy6+{6ב!i2 Ŵz (s0k#=#rl2J4RL@ `eת)=~1pǡ" 2Rל\^ɽ׏: x-w©2FD7nJh -ί'~%|(~n?:xO^qB0G-y^ jbJ%5yPhBlNP6jI{d@8S]zNc^F~zj/Gj eCǪ]D v&Ds6yסYթ`u Ss99RxP^zm,| C>Ü+Jchi:Qh/ |f&|beJ7<S{^_]~:"Z<_֢df!br}?nhs22x<#>M- %1xbxS;8Q| VdTQnc*E)N7\ao F"dGNBᓧzY"GhoI֏̕wD$eL V(a3Ӓ7[߫ݻe5FPmjqiwFsb0I?g1J*&{`\f*>lΰdw䭚,6gpiI$l`(8 MԱ/ -ݐMYKur'ŚO,e};yZp=u_k-(VQm*Cg%j1.kH5 fs35oy)SO>eRK =b{3R28CU즚~؃E%o{!A%}}6L LPB9xέL|0*<^ncѢy,5wmb^}X2V+LLu6vX@T=0GnZ}gW͊G$8SP^Pnx҈,1rm,.IRT`iM[N8 CeYEysé(uW<ku"w"Hnvr$t\O8?~P_+X9- `g #endstream endobj 9 0 obj << /Type /Font /Subtype /Type1 /Encoding 470 0 R /FirstChar 11 /LastChar 122 /Widths 471 0 R /BaseFont /SHAWGP+CMBX12 /FontDescriptor 7 0 R >> endobj 7 0 obj << /Ascent 694 /CapHeight 686 /Descent -194 /FontName /SHAWGP+CMBX12 /ItalicAngle 0 /StemV 109 /XHeight 444 /FontBBox [-53 -251 1139 750] /Flags 4 /CharSet (/ff/fi/fl/quoteright/parenleft/parenright/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/equal/A/B/C/D/E/F/H/I/J/K/L/M/N/O/P/R/S/T/U/V/W/Z/bracketleft/bracketright/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z) /FontFile 8 0 R >> endobj 471 0 obj [656 625 625 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 312 437 437 0 0 312 375 312 562 562 562 562 562 562 562 562 562 562 562 312 0 0 875 0 0 0 850 800 812 862 738 707 0 880 419 581 881 676 1067 880 845 769 0 839 625 782 865 850 1162 0 0 687 312 0 312 0 0 0 547 625 500 625 513 344 562 625 312 344 594 312 937 625 562 625 594 459 444 437 625 594 812 594 594 500 ] endobj 470 0 obj << /Type /Encoding /Differences [ 0 /.notdef 11/ff/fi/fl 14/.notdef 39/quoteright/parenleft/parenright 42/.notdef 44/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon 59/.notdef 61/equal 62/.notdef 65/A/B/C/D/E/F 71/.notdef 72/H/I/J/K/L/M/N/O/P 81/.notdef 82/R/S/T/U/V/W 88/.notdef 90/Z/bracketleft 92/.notdef 93/bracketright 94/.notdef 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 123/.notdef] >> endobj 5 0 obj << /Length1 2056 /Length2 15081 /Length3 532 /Length 16215 /Filter /FlateDecode >> stream xeT]ͶC!CЅ{pww4hp\~眝mhz1j*1P΅ `adfEsXۉy,<<I1臗 fdin' b t41(XmAsTM,.?O8T@'7)# ` 4C`ǒ=¦3trP& d` 4C`dRaO.jc)5ldki .@')j˛ʸXؙt*YX̌lL PeI^^DCMz1%#K;5OOdeOKK 0rr2DqYv@dT_? _BVZA6A As q\m vœ&S{#?!=?jٙLFN@;_Qd!{[[?6;Hbo'`r1r0y@f/? G.N2rS=K2@vA}7: !?jglcC38rk1q rg%ƽ&r|\{Gt k͔'_ NhxFs<9qRLu#\#+Ϫۛh(3k-mYHrBb8lJ~@8>}GĒ#̚apT1~;zcJ* ͂[2Wj}aVJF'n-hI(uaL 1c--(8!^7)_冓D{$z/%y罺;tGOS7&w_va3̹:L]&ω̲J L|pTn= O\ t-on J7"a)~4KsTeMɳO'Y*?zRTIЕ d)*#h f*_c>$uzCl]4\ nLeaڮ WvW+pstUC[BB21"RedJ<.좎g/r,+1T\2;eȃֺD⭬G~\RR&` p(6NxՍ!N3I0 Ԧl:p|#e1"3Na)~u%QH:]5X[ 4)[9B}Pckbw'71 ,fh.iJ7P^h%G(k9}$+3wt^7i2, k~O/S]ke49fhs*19韛F;#x?}NQ<2>uZ&uOVG/T3豀vz 6s6/$U}?s? h" T'qҞq4vl / `) >r^x9>br}hއ7d&h\oߢ'w}6椨V7LQJ۠= Kgf'l)-B;zI"융KMTKgwO,+kRGҜj>'Υ_:ȹN)PLF 5^d!~i7k4-Y0KD=.Ou.`,Lֺ8HQjaO dR<"m:gpqNO2v1.eKQo.d,0S>o*MCiIwuyI\4/qSGz ˙7<: {3:_ |ss[Hkkyn޺<Si|BY)MJjH@9\˄vfl+ײBDǯ\{=Jqu7̖84O n# ghrE5_HOZV'Uw!jhi?B-9UOm}py}v%x|KQݕ4YK2t4'R0\&f. }V0pgXJR&_A/\\oX3>_| hQ/gF5+д~IZV7 OP`RuƟTYJzdhwj֕BRZ$13ԩHs'|w; [0LN `XqP s!tM57~# 5|"YH׏ v\AC$"R,09'9FH_.l:)E#Q(9F"s ^ eH[(ƬEAfj!ЀYp/Ո]dj5N|4 -|塒8|=ױ1)WhzFz UH·!GeQ Zo-Gߕ|LAɤ54 X:+?CԌ 0ƫ}ʗvXfIiH'*- NEY$gl]T`'a% PzZැA[i౩6~:y{4wM:@A`e cǪfE#v?΍D`l|HԾ/p3%N_zcg)v-o"ᅬ#G ko|yf}W]\2IvSEGzۣjBjz' xN$Ԥh6\4˶Zg1]/ì[/7vl rb73+bnQ:K'}`']6 ۯA75>Uqw3gpΆ4x"®eC8x ĠtBzbMGvZC\n\%6{9Ky8i w{ fJ]/M.s*scx]RH EA'U"A㹐ӚqYg}h~C(b:Jؐ&Xj f}VQ"0f٣E:Wڼ!/LVuCI_+*o ʔ P2Dp>a(;Q_&Ay)S`}%a-~M^6n%[u#(@$u8~=’c)L>ZJbSZ E8œjos[((J5gUA\#~bFQmYcɂF*%α-]N",WkTÑMFUďv. Q\dPӸHy`S3qv7P*]J<__ dÕt5*Ōcjj";=FDYTjy{E,ƠwSF@WPxkWV-kJ><2 y0#GHSXQ\}H)0V{jp6e25zxx&Ks{ɤG4d>ʙC68"Sk)LY.SEaD 10@exFKN3P=ͅ $Z1 V9lh¶&\7A Cy誕!pӏjx#n ,JmM~1)MmءN.l@Yx:=&v:>{VB0+1f1Nr /M1ئ+M%Os> JKz+)U*7)o7ccZ-x-EJ>R{cb#D}$N$ȑ&. <bsZ1gE '=a%LzsZ1sZ*Od 9iE֕CO}-/;\+h,۷cۣ^T{~}A|J~-r)_h- )o![2BЙsTkmwf-<aC"#GF aZkW3*OW.ֹ ;8o.Sʔ<\SmV q֘ J~ ~%uOJfaiN=٭e!WG '=_7r5bHT2!(LAW-~) =:2h68h=|3,IQ˫OQ RBb(iԼ((O"3c2RH 4`.J][ L(qZ-mDWqcH|)Kxv]l Ҿ탈E1=B/?r.k4ofw׃[OtK7$:KxPMS^CprB|*~hs;z)?,[q&ãRj3W[,u~^wbgkL&e{J0'f+ 4uJNO`CjY<"D~Mt cre2ro4tcwSs/GL*$\yIq22M'9QF=>lI&yچ% Z'MS$Xg(#iL^pȻY,, 鵠yPs!= |4pH{L;P:Ts;p0˧x4QD=m(N&_T ֽcǷoCK  YI3J^Uu'm}pzBvV&qF#bVaz-Le }բ9aD\a(?vՎ=`#T|вmgŧ1aW"qEb_]E%-jpǷFGUi[ZZ42N{^q\Tͨ^_W8~T. 7ix}]!eN-)}MN웾W{oLLfvuDԌ}NC1 {:t!]Mf[=cvf=YH]޽c1"t!Sky>SVi3;&й1xt`w%)kUCqKgvH["Iyipmq3/:) tSB xyzp813ou=Gb2NXIzZ#O/涙^ sD(341 KHH]&#b{畴 Y~ɞe[ RMЁfQh_m9 7`+)ؾ}WGH?S6u]}¶",U]RNF\cbkS"/(sWl("+ѕkwKԢ&SW$= d]w1{[mrAVZXj+ځ~xN,_+ڏE^i8z}NwUe +*P0kc6y:Gh 6:Izֈ\$^cdhwo43-=Э_r{zihS^,pn)VJTΒ Gt+gd5* j>'bA _S_Ygm`r: ѸСhN>?fֿo|E%2&2tHi4'r΍~2oq:bTv>D"(5 ]ШCͶ| Ljg(}bBQ/e`T o@곪/i_ߧ$:}l~)VTN}giDz[s' 0:ra)"j\x^Un/?fbCSkbE&S [Gq}9XT>K8lov.^%IuCc!n:2\) <]SHȶ x> Bg?uh60;b8m Vm 0.C!ޔ} йz$~L^Zlv1k$pP_1Ѭ7蕠CHΤ ^^u=Si9iY܋V?!PqTNW\* 2Vmή ҭ!{uj/콠W "&sWΞYyq. K2Cp8Ff.%0gƚE%\Gr&g nPБ2^URk+!WwyU94KԿ; z se1 BH`=lʲ()\^y}.BA> ƒ̙s?vwMC+]HylU\w O,N<"?-OEcb֠bVǰv m0USjT@MTA!om\!6ܡ3yRzq< a_1\LҏmW Җr8:cMGl+̳ƄYbW)7$P'F h8*a`>7Y5ZϥX] i*}TTbWBq*y1YL6DUo7w|C%e"TZYПDt,#Q:nTi/3m[梁fa$Ɠ. .+ ;ziD\BQ:[!gDഥt)POD >١>d[(_h ݔcq;=P^f0 r2L&@QJ3~( O047SO$\ך-B{PV7!S cM65X- 'DGk75ͱSS^3׏vW'q~|+XsN"+.)Y2A* ];&) !Ʒw[=}J/ڂhjA9FWE W&W-wכ;@ZIKf4%3fz= CR g;XKdm]غ_y莬33H8~n=_>^MEnZ3s,N w< E/~5ڶyH8]%>1A+oji_ILi R0.jXS3Q+U۝'ʅѲ|3#"DT+~R,_ح9NKFb t)],'H0>X+ Sh{2mb>ľC5C) >椫9cJZgVVgnnThzfݫj w XoLxuӿ՛lYyIN\q~kpln(5tD ZXfʀg{1$k |-0` X}9`vʼ-̛ɠ$i6 "l%}kM8s͘g+ror%jA!Y|lQ5Symbpo,M!U-P0?9ytZ)9 VSDrzߪ'2 'U{Xsc)y&W!ft1\Րs%42 g<޶,V+Rf7Zd !FhЌ@qDw~! ֏a">2h7.e1븏b~Z`ǁ]pG|A܍PXiw鴨 ;<`\#0Q7*(I˙0"/ςL8PV*F93!}g֥Im,="EB=mỗ6Ӧ릙ᨴ q<@yOɢukASOws?9E1T~b:Mi(`Gm"[IOOvoK8E6ANi{Ě' '!>neo:}ŝoϤ.-S :I b\ek<\k؎̨+kg}M;8L CdhFOKvEN kԫv̿Ua(cL NTW߻ ,4;.% {u[(and#^iy<ּ+PE>Zs`S,BGϩ vJPÂ7eFL(vbCBn<˃xnz2[|GĝAb$)ATK3b'+Xۆ!@:ZAguR!-dXwadL8׏-} OI4ըJgH(U6XEwG Uq>Y*Le\QHZi{j{]`Nݠ딨hdM|A\X \97 |15ѨHi8. |i=[ #W"҃s#מAtYAU MG&C9\8KdvC/PY 3;gψf ds@n*=ӶL\0e#ѪF45z ~Gl$6_+KTk|!dNjv҃AW<XP/LYOJ'ڂ[W /{U2"J_ fF50S7X<&83ѫ!24[ROuꐽDw1ی_=`CoUΣ \di.28,ŭwQr<|Kގbӷ>aq+ǝ߆1a E~[q4 :A8ϻ@{5}İՌB#SJE_@J 4:_^yNB \(윹@\CR5{pi !IZGl bַM߹DN{+OH %gՐqwΚj Na]ݬH>},kRڷp6U0r eZH+/ZB kzeSDdG1rTZ14~ nҴ_#"¡{$4Ns l<n2"<6NyG/-[pT*?Xgce'F#F!sah%;w}}[1h k߶4 *֦{ c<6Eٜ' |%;3+SE'#Hݭ%!1ef|53a."Iigi 7ʾ*9"s#B!"`J3[-̮ -/,@U^=c{V`5f=]&t^jM+ȏK@(L B|D*7{|U n[l)a0z9C$Ц /NR'8]`TXq[*T5Tai`2&-~~_-C 0|%wgFYMm~חi0mw"]?vb03{,?.x+Hʌ'*S˸BH_ ]4(GlZAXZZŘssJ싫WT&.q$:JW_OGz-U>6;TimHZ]'L]:|bL햧zҔ(m|Jpֈq+[6\+P6ֻ۞OQ@GE46nDDB{Hv䏴;\R5B-4*K{G4sDR5@ڶe8]a%= #z(WEg㰯,㗲ba\&ּMPc9$md[MteNͼei qNX衁f9Ca9dm懟څ>aduxk7cA,cyJ:)?O˗ko8ʥ6 gk}$XIIԄ_Fz|dךxw[3_, 2I}`'jS5P#ۯ*_8W6Zkl\^"%yÒ !itOS*OM,Nn4_0ܮnSL'+SwZQWwyI'b:XF5yx/%=$gW_X7&F-YN֏oV!:۩jSLwLWp Ėa?E Gb;y!:־#n&B͋WGehHf=2ΌQ1 ֖^1cM?s#*Ғ g Kj7ŕk$"Qϡ"4Y\?o<ΐFDw םI/^ uőZkGr@Shn crʾmtg!5t8^^|J`IlY_)T (xcxV s>Z\͜XО "=: ~h '12np\Au{> ŇMEuWՄ7=a鱉Y{\6$[䞢cd G0JϴM[ж1XABtì?X[й?B NV;$ԃQvfp&%bkػ0.xx W!6(Qy 605C0skZ C/Ҙ6_|])tw[?ic~8@O傲*+O |TA+dMfAKXL?@/<0`f2(ܽR[rPȇKYcWZ qʠg7,9r3EMo8t9Kine!yLHmX.xUY(3+󥩃Ѭ?u *F̗=o*OMY*Vd S*rlYX܈t!l(]7) F^EjqNS`MUJSu_6Ƈ缛VSc'܂ţw\%8v,#e:sF~e~ #%N{ N6N)T ~@r*AKk4<:;֞nqcÌO1XOT0=bk'AK<^ߏMބ27^LJ; w5ֲ ~sGd%C~}x9ׁaCc޻&PVniFڸ6I6>zR9mSrV1bߵ}`,B1zZZgIs/C*&wiġa Xu7 pL\;>Kٯ+'x8Ý|^FqDIr nhSevPT!wާ׻Lk3&CT2N!m{tyV'D(/ Nw"Z]=6;2n{QzvmFo$B9 k 5Xe!I9ǼR F9Xd TIU-c$P{?fMDGޤ QcSq]|)ksb*y*z5(?F,Mb˦Z7"j]yc WU%;9=/܆o} o7R-Q-:wRcE@rpx3ǡM-U:u߼c.ڨ?M?gvy4Wj=G\&FO ubn8WP5AD|#ڵ[w `|Ige"ɔ+ c[ <vg{6jy4T|܃m~{@r%UW#h~0ܘׯt&Rf[vR_\)9ūs7Ln -uW#72c$/w}Fqzlϵ$H,@ot8h ,pJ=R!Q׍HZe`ag8it6VNCgC.k]_hbokd9zendstream endobj 6 0 obj << /Type /Font /Subtype /Type1 /Encoding 472 0 R /FirstChar 11 /LastChar 122 /Widths 473 0 R /BaseFont /LLAVTJ+CMR10 /FontDescriptor 4 0 R >> endobj 4 0 obj << /Ascent 694 /CapHeight 683 /Descent -194 /FontName /LLAVTJ+CMR10 /ItalicAngle 0 /StemV 69 /XHeight 431 /FontBBox [-251 -250 1009 969] /Flags 4 /CharSet (/ff/fi/fl/ffi/exclam/numbersign/dollar/quoteright/parenleft/parenright/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/equal/question/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/R/S/T/U/V/W/Y/Z/bracketleft/bracketright/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z) /FontFile 5 0 R >> endobj 473 0 obj [583 556 556 833 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 278 0 833 500 0 0 278 389 389 0 0 278 333 278 500 500 500 500 500 500 500 500 500 500 500 278 278 0 778 0 472 0 750 708 722 764 681 653 785 750 361 514 778 625 917 750 778 681 0 736 556 722 750 750 1028 0 750 611 278 0 278 0 0 278 500 556 444 556 444 306 500 556 278 306 528 278 833 556 500 556 528 392 394 389 556 528 722 528 528 444 ] endobj 472 0 obj << /Type /Encoding /Differences [ 0 /.notdef 11/ff/fi/fl/ffi 15/.notdef 33/exclam 34/.notdef 35/numbersign/dollar 37/.notdef 39/quoteright/parenleft/parenright 42/.notdef 44/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon 60/.notdef 61/equal 62/.notdef 63/question 64/.notdef 65/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P 81/.notdef 82/R/S/T/U/V/W 88/.notdef 89/Y/Z/bracketleft 92/.notdef 93/bracketright 94/.notdef 96/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 123/.notdef] >> endobj 10 0 obj << /Type /Pages /Count 6 /Parent 474 0 R /Kids [2 0 R 12 0 R 15 0 R 56 0 R 104 0 R 333 0 R] >> endobj 340 0 obj << /Type /Pages /Count 6 /Parent 474 0 R /Kids [337 0 R 342 0 R 347 0 R 351 0 R 355 0 R 358 0 R] >> endobj 366 0 obj << /Type /Pages /Count 6 /Parent 474 0 R /Kids [362 0 R 368 0 R 372 0 R 376 0 R 380 0 R 392 0 R] >> endobj 397 0 obj << /Type /Pages /Count 6 /Parent 474 0 R /Kids [395 0 R 399 0 R 402 0 R 409 0 R 413 0 R 417 0 R] >> endobj 422 0 obj << /Type /Pages /Count 6 /Parent 474 0 R /Kids [420 0 R 424 0 R 427 0 R 430 0 R 433 0 R 437 0 R] >> endobj 447 0 obj << /Type /Pages /Count 3 /Parent 474 0 R /Kids [444 0 R 449 0 R 453 0 R] >> endobj 474 0 obj << /Type /Pages /Count 33 /Kids [10 0 R 340 0 R 366 0 R 397 0 R 422 0 R 447 0 R] >> endobj 475 0 obj << /Type /Outlines /First 129 0 R /Last 330 0 R /Count 7 >> endobj 330 0 obj << /Title 331 0 R /A 329 0 R /Parent 475 0 R /Prev 327 0 R >> endobj 327 0 obj << /Title 328 0 R /A 326 0 R /Parent 475 0 R /Prev 315 0 R /Next 330 0 R >> endobj 324 0 obj << /Title 325 0 R /A 323 0 R /Parent 315 0 R /Prev 321 0 R >> endobj 321 0 obj << /Title 322 0 R /A 320 0 R /Parent 315 0 R /Prev 318 0 R /Next 324 0 R >> endobj 318 0 obj << /Title 319 0 R /A 317 0 R /Parent 315 0 R /Next 321 0 R >> endobj 315 0 obj << /Title 316 0 R /A 314 0 R /Parent 475 0 R /Prev 204 0 R /Next 327 0 R /First 318 0 R /Last 324 0 R /Count -3 >> endobj 312 0 obj << /Title 313 0 R /A 311 0 R /Parent 261 0 R /Prev 309 0 R >> endobj 309 0 obj << /Title 310 0 R /A 308 0 R /Parent 261 0 R /Prev 306 0 R /Next 312 0 R >> endobj 306 0 obj << /Title 307 0 R /A 305 0 R /Parent 261 0 R /Prev 303 0 R /Next 309 0 R >> endobj 303 0 obj << /Title 304 0 R /A 302 0 R /Parent 261 0 R /Prev 300 0 R /Next 306 0 R >> endobj 300 0 obj << /Title 301 0 R /A 299 0 R /Parent 261 0 R /Prev 297 0 R /Next 303 0 R >> endobj 297 0 obj << /Title 298 0 R /A 296 0 R /Parent 261 0 R /Prev 294 0 R /Next 300 0 R >> endobj 294 0 obj << /Title 295 0 R /A 293 0 R /Parent 261 0 R /Prev 291 0 R /Next 297 0 R >> endobj 291 0 obj << /Title 292 0 R /A 290 0 R /Parent 261 0 R /Prev 288 0 R /Next 294 0 R >> endobj 288 0 obj << /Title 289 0 R /A 287 0 R /Parent 261 0 R /Prev 285 0 R /Next 291 0 R >> endobj 285 0 obj << /Title 286 0 R /A 284 0 R /Parent 261 0 R /Prev 282 0 R /Next 288 0 R >> endobj 282 0 obj << /Title 283 0 R /A 281 0 R /Parent 261 0 R /Prev 279 0 R /Next 285 0 R >> endobj 279 0 obj << /Title 280 0 R /A 278 0 R /Parent 261 0 R /Prev 276 0 R /Next 282 0 R >> endobj 276 0 obj << /Title 277 0 R /A 275 0 R /Parent 261 0 R /Prev 273 0 R /Next 279 0 R >> endobj 273 0 obj << /Title 274 0 R /A 272 0 R /Parent 261 0 R /Prev 270 0 R /Next 276 0 R >> endobj 270 0 obj << /Title 271 0 R /A 269 0 R /Parent 261 0 R /Prev 267 0 R /Next 273 0 R >> endobj 267 0 obj << /Title 268 0 R /A 266 0 R /Parent 261 0 R /Prev 264 0 R /Next 270 0 R >> endobj 264 0 obj << /Title 265 0 R /A 263 0 R /Parent 261 0 R /Next 267 0 R >> endobj 261 0 obj << /Title 262 0 R /A 260 0 R /Parent 204 0 R /Prev 246 0 R /First 264 0 R /Last 312 0 R /Count -17 >> endobj 258 0 obj << /Title 259 0 R /A 257 0 R /Parent 246 0 R /Prev 255 0 R >> endobj 255 0 obj << /Title 256 0 R /A 254 0 R /Parent 246 0 R /Prev 252 0 R /Next 258 0 R >> endobj 252 0 obj << /Title 253 0 R /A 251 0 R /Parent 246 0 R /Prev 249 0 R /Next 255 0 R >> endobj 249 0 obj << /Title 250 0 R /A 248 0 R /Parent 246 0 R /Next 252 0 R >> endobj 246 0 obj << /Title 247 0 R /A 245 0 R /Parent 204 0 R /Prev 207 0 R /Next 261 0 R /First 249 0 R /Last 258 0 R /Count -4 >> endobj 243 0 obj << /Title 244 0 R /A 242 0 R /Parent 207 0 R /Prev 240 0 R >> endobj 240 0 obj << /Title 241 0 R /A 239 0 R /Parent 207 0 R /Prev 237 0 R /Next 243 0 R >> endobj 237 0 obj << /Title 238 0 R /A 236 0 R /Parent 207 0 R /Prev 234 0 R /Next 240 0 R >> endobj 234 0 obj << /Title 235 0 R /A 233 0 R /Parent 207 0 R /Prev 231 0 R /Next 237 0 R >> endobj 231 0 obj << /Title 232 0 R /A 230 0 R /Parent 207 0 R /Prev 228 0 R /Next 234 0 R >> endobj 228 0 obj << /Title 229 0 R /A 227 0 R /Parent 207 0 R /Prev 225 0 R /Next 231 0 R >> endobj 225 0 obj << /Title 226 0 R /A 224 0 R /Parent 207 0 R /Prev 222 0 R /Next 228 0 R >> endobj 222 0 obj << /Title 223 0 R /A 221 0 R /Parent 207 0 R /Prev 219 0 R /Next 225 0 R >> endobj 219 0 obj << /Title 220 0 R /A 218 0 R /Parent 207 0 R /Prev 216 0 R /Next 222 0 R >> endobj 216 0 obj << /Title 217 0 R /A 215 0 R /Parent 207 0 R /Prev 213 0 R /Next 219 0 R >> endobj 213 0 obj << /Title 214 0 R /A 212 0 R /Parent 207 0 R /Prev 210 0 R /Next 216 0 R >> endobj 210 0 obj << /Title 211 0 R /A 209 0 R /Parent 207 0 R /Next 213 0 R >> endobj 207 0 obj << /Title 208 0 R /A 206 0 R /Parent 204 0 R /Next 246 0 R /First 210 0 R /Last 243 0 R /Count -12 >> endobj 204 0 obj << /Title 205 0 R /A 203 0 R /Parent 475 0 R /Prev 135 0 R /Next 315 0 R /First 207 0 R /Last 261 0 R /Count -3 >> endobj 201 0 obj << /Title 202 0 R /A 200 0 R /Parent 183 0 R /Prev 198 0 R >> endobj 198 0 obj << /Title 199 0 R /A 197 0 R /Parent 183 0 R /Prev 195 0 R /Next 201 0 R >> endobj 195 0 obj << /Title 196 0 R /A 194 0 R /Parent 183 0 R /Prev 192 0 R /Next 198 0 R >> endobj 192 0 obj << /Title 193 0 R /A 191 0 R /Parent 183 0 R /Prev 189 0 R /Next 195 0 R >> endobj 189 0 obj << /Title 190 0 R /A 188 0 R /Parent 183 0 R /Prev 186 0 R /Next 192 0 R >> endobj 186 0 obj << /Title 187 0 R /A 185 0 R /Parent 183 0 R /Next 189 0 R >> endobj 183 0 obj << /Title 184 0 R /A 182 0 R /Parent 135 0 R /Prev 162 0 R /First 186 0 R /Last 201 0 R /Count -6 >> endobj 180 0 obj << /Title 181 0 R /A 179 0 R /Parent 162 0 R /Prev 177 0 R >> endobj 177 0 obj << /Title 178 0 R /A 176 0 R /Parent 162 0 R /Prev 174 0 R /Next 180 0 R >> endobj 174 0 obj << /Title 175 0 R /A 173 0 R /Parent 162 0 R /Prev 171 0 R /Next 177 0 R >> endobj 171 0 obj << /Title 172 0 R /A 170 0 R /Parent 162 0 R /Prev 168 0 R /Next 174 0 R >> endobj 168 0 obj << /Title 169 0 R /A 167 0 R /Parent 162 0 R /Prev 165 0 R /Next 171 0 R >> endobj 165 0 obj << /Title 166 0 R /A 164 0 R /Parent 162 0 R /Next 168 0 R >> endobj 162 0 obj << /Title 163 0 R /A 161 0 R /Parent 135 0 R /Prev 138 0 R /Next 183 0 R /First 165 0 R /Last 180 0 R /Count -6 >> endobj 159 0 obj << /Title 160 0 R /A 158 0 R /Parent 138 0 R /Prev 156 0 R >> endobj 156 0 obj << /Title 157 0 R /A 155 0 R /Parent 138 0 R /Prev 153 0 R /Next 159 0 R >> endobj 153 0 obj << /Title 154 0 R /A 152 0 R /Parent 138 0 R /Prev 150 0 R /Next 156 0 R >> endobj 150 0 obj << /Title 151 0 R /A 149 0 R /Parent 138 0 R /Prev 147 0 R /Next 153 0 R >> endobj 147 0 obj << /Title 148 0 R /A 146 0 R /Parent 138 0 R /Prev 144 0 R /Next 150 0 R >> endobj 144 0 obj << /Title 145 0 R /A 143 0 R /Parent 138 0 R /Prev 141 0 R /Next 147 0 R >> endobj 141 0 obj << /Title 142 0 R /A 140 0 R /Parent 138 0 R /Next 144 0 R >> endobj 138 0 obj << /Title 139 0 R /A 137 0 R /Parent 135 0 R /Next 162 0 R /First 141 0 R /Last 159 0 R /Count -7 >> endobj 135 0 obj << /Title 136 0 R /A 134 0 R /Parent 475 0 R /Prev 132 0 R /Next 204 0 R /First 138 0 R /Last 183 0 R /Count -3 >> endobj 132 0 obj << /Title 133 0 R /A 131 0 R /Parent 475 0 R /Prev 129 0 R /Next 135 0 R >> endobj 129 0 obj << /Title 130 0 R /A 128 0 R /Parent 475 0 R /Next 132 0 R >> endobj 476 0 obj << /Names [(-1) 17 0 R (-2) 58 0 R (-3) 106 0 R (1) 49 0 R (10) 374 0 R (11) 378 0 R (12) 94 0 R (13) 95 0 R (14) 96 0 R (15) 97 0 R (16) 98 0 R (17) 99 0 R (18) 415 0 R (19) 100 0 R (2) 50 0 R (20) 101 0 R (21) 102 0 R (22) 122 0 R (23) 123 0 R (24) 435 0 R (25) 124 0 R (26) 125 0 R (27) 126 0 R (28) 127 0 R (3) 51 0 R (4) 52 0 R (5) 53 0 R (6) 54 0 R (7) 360 0 R (8) 93 0 R (9) 370 0 R (AntiC command line options) 440 0 R (Bugs detected by AntiC) 344 0 R (Bugs detected by Jlint) 364 0 R (Bugs in tokens) 345 0 R (Command line options) 439 0 R (Data flow) 411 0 R (How to build) 451 0 R (Inheritance) 407 0 R (Introduction) 339 0 R (Jlint) 335 0 R (Jlint command line options) 441 0 R (Jlint messages hierarchy) 446 0 R (Operators priorities) 349 0 R (Release notes) 455 0 R (Statement body) 353 0 R (Synchronization) 365 0 R] /Limits [(-1) (Synchronization)] >> endobj 477 0 obj << /Kids [476 0 R] >> endobj 478 0 obj << /Dests 477 0 R >> endobj 479 0 obj << /Type /Catalog /Pages 474 0 R /Outlines 475 0 R /Names 478 0 R /PageMode /UseOutlines /APDF.Fullbanner (This is pdfTeX, Version 3.14159-1.00a-pretest-20011114-ojmw) >> endobj 480 0 obj << /Producer (pdfTeX-1.0a-pdfcrypt) /CreationDate (D:20040621224000) >> endobj xref 0 481 0000000000 65535 f 0000000515 00000 n 0000000403 00000 n 0000000009 00000 n 0000176575 00000 n 0000160083 00000 n 0000176418 00000 n 0000158793 00000 n 0000146138 00000 n 0000158635 00000 n 0000178033 00000 n 0000001397 00000 n 0000001282 00000 n 0000000593 00000 n 0000006513 00000 n 0000002991 00000 n 0000001465 00000 n 0000006459 00000 n 0000145821 00000 n 0000143999 00000 n 0000145660 00000 n 0000003293 00000 n 0000003419 00000 n 0000003543 00000 n 0000143155 00000 n 0000136937 00000 n 0000142995 00000 n 0000003669 00000 n 0000003796 00000 n 0000003923 00000 n 0000004049 00000 n 0000004175 00000 n 0000004302 00000 n 0000004429 00000 n 0000004556 00000 n 0000004683 00000 n 0000004810 00000 n 0000004937 00000 n 0000005062 00000 n 0000005189 00000 n 0000005316 00000 n 0000005443 00000 n 0000005570 00000 n 0000005697 00000 n 0000005824 00000 n 0000005951 00000 n 0000006078 00000 n 0000006205 00000 n 0000006332 00000 n 0000024234 00000 n 0000028151 00000 n 0000030534 00000 n 0000032973 00000 n 0000035093 00000 n 0000036833 00000 n 0000012897 00000 n 0000008945 00000 n 0000006616 00000 n 0000012843 00000 n 0000009268 00000 n 0000009393 00000 n 0000135839 00000 n 0000124904 00000 n 0000135679 00000 n 0000009520 00000 n 0000009648 00000 n 0000009776 00000 n 0000009904 00000 n 0000010032 00000 n 0000010160 00000 n 0000010288 00000 n 0000010416 00000 n 0000010544 00000 n 0000010672 00000 n 0000010800 00000 n 0000123395 00000 n 0000108999 00000 n 0000123235 00000 n 0000010927 00000 n 0000011055 00000 n 0000011183 00000 n 0000011311 00000 n 0000011438 00000 n 0000011566 00000 n 0000011694 00000 n 0000011822 00000 n 0000011949 00000 n 0000012077 00000 n 0000012205 00000 n 0000012332 00000 n 0000012459 00000 n 0000012587 00000 n 0000012715 00000 n 0000041047 00000 n 0000051959 00000 n 0000054986 00000 n 0000057725 00000 n 0000060128 00000 n 0000063434 00000 n 0000066817 00000 n 0000071256 00000 n 0000073235 00000 n 0000075371 00000 n 0000016535 00000 n 0000014296 00000 n 0000013024 00000 n 0000016479 00000 n 0000014546 00000 n 0000014675 00000 n 0000014804 00000 n 0000014933 00000 n 0000015061 00000 n 0000015190 00000 n 0000015319 00000 n 0000015448 00000 n 0000015577 00000 n 0000015706 00000 n 0000015835 00000 n 0000015964 00000 n 0000016093 00000 n 0000016222 00000 n 0000016350 00000 n 0000077823 00000 n 0000080281 00000 n 0000084832 00000 n 0000086682 00000 n 0000089611 00000 n 0000090977 00000 n 0000016651 00000 n 0000185201 00000 n 0000016690 00000 n 0000016715 00000 n 0000185108 00000 n 0000016754 00000 n 0000016786 00000 n 0000184976 00000 n 0000016825 00000 n 0000016867 00000 n 0000184858 00000 n 0000016906 00000 n 0000016940 00000 n 0000184779 00000 n 0000016979 00000 n 0000017019 00000 n 0000184686 00000 n 0000017058 00000 n 0000017127 00000 n 0000184593 00000 n 0000017166 00000 n 0000017222 00000 n 0000184500 00000 n 0000017261 00000 n 0000017313 00000 n 0000184407 00000 n 0000017352 00000 n 0000017403 00000 n 0000184314 00000 n 0000017442 00000 n 0000017509 00000 n 0000184235 00000 n 0000017548 00000 n 0000017632 00000 n 0000184103 00000 n 0000017671 00000 n 0000017710 00000 n 0000184024 00000 n 0000017749 00000 n 0000017819 00000 n 0000183931 00000 n 0000017858 00000 n 0000017936 00000 n 0000183838 00000 n 0000017975 00000 n 0000018048 00000 n 0000183745 00000 n 0000018087 00000 n 0000018138 00000 n 0000183652 00000 n 0000018177 00000 n 0000018246 00000 n 0000183573 00000 n 0000018285 00000 n 0000018357 00000 n 0000183455 00000 n 0000018396 00000 n 0000018430 00000 n 0000183376 00000 n 0000018469 00000 n 0000018528 00000 n 0000183283 00000 n 0000018567 00000 n 0000018624 00000 n 0000183190 00000 n 0000018663 00000 n 0000018736 00000 n 0000183097 00000 n 0000018775 00000 n 0000018825 00000 n 0000183004 00000 n 0000018864 00000 n 0000018907 00000 n 0000182925 00000 n 0000018946 00000 n 0000019008 00000 n 0000182793 00000 n 0000019047 00000 n 0000019089 00000 n 0000182674 00000 n 0000019128 00000 n 0000019163 00000 n 0000182595 00000 n 0000019203 00000 n 0000019289 00000 n 0000182502 00000 n 0000019329 00000 n 0000019435 00000 n 0000182409 00000 n 0000019475 00000 n 0000019586 00000 n 0000182316 00000 n 0000019626 00000 n 0000019710 00000 n 0000182223 00000 n 0000019750 00000 n 0000019827 00000 n 0000182130 00000 n 0000019867 00000 n 0000019974 00000 n 0000182037 00000 n 0000020014 00000 n 0000020106 00000 n 0000181944 00000 n 0000020146 00000 n 0000020185 00000 n 0000181851 00000 n 0000020225 00000 n 0000020310 00000 n 0000181758 00000 n 0000020350 00000 n 0000020438 00000 n 0000181665 00000 n 0000020478 00000 n 0000020557 00000 n 0000181586 00000 n 0000020597 00000 n 0000020675 00000 n 0000181454 00000 n 0000020715 00000 n 0000020746 00000 n 0000181375 00000 n 0000020786 00000 n 0000020886 00000 n 0000181282 00000 n 0000020926 00000 n 0000021005 00000 n 0000181189 00000 n 0000021045 00000 n 0000021116 00000 n 0000181110 00000 n 0000021156 00000 n 0000021223 00000 n 0000180991 00000 n 0000021263 00000 n 0000021292 00000 n 0000180912 00000 n 0000021332 00000 n 0000021458 00000 n 0000180819 00000 n 0000021498 00000 n 0000021563 00000 n 0000180726 00000 n 0000021603 00000 n 0000021649 00000 n 0000180633 00000 n 0000021689 00000 n 0000021735 00000 n 0000180540 00000 n 0000021775 00000 n 0000021826 00000 n 0000180447 00000 n 0000021866 00000 n 0000021924 00000 n 0000180354 00000 n 0000021964 00000 n 0000022028 00000 n 0000180261 00000 n 0000022068 00000 n 0000022157 00000 n 0000180168 00000 n 0000022197 00000 n 0000022267 00000 n 0000180075 00000 n 0000022307 00000 n 0000022368 00000 n 0000179982 00000 n 0000022408 00000 n 0000022470 00000 n 0000179889 00000 n 0000022510 00000 n 0000022589 00000 n 0000179796 00000 n 0000022629 00000 n 0000022691 00000 n 0000179703 00000 n 0000022731 00000 n 0000022780 00000 n 0000179610 00000 n 0000022820 00000 n 0000022876 00000 n 0000179517 00000 n 0000022916 00000 n 0000022998 00000 n 0000179438 00000 n 0000023038 00000 n 0000023125 00000 n 0000179306 00000 n 0000023165 00000 n 0000023205 00000 n 0000179227 00000 n 0000023245 00000 n 0000023291 00000 n 0000179134 00000 n 0000023331 00000 n 0000023377 00000 n 0000179055 00000 n 0000023417 00000 n 0000023461 00000 n 0000178962 00000 n 0000023501 00000 n 0000023557 00000 n 0000178883 00000 n 0000023597 00000 n 0000024346 00000 n 0000024116 00000 n 0000023630 00000 n 0000024289 00000 n 0000028263 00000 n 0000028032 00000 n 0000024426 00000 n 0000028206 00000 n 0000178144 00000 n 0000030703 00000 n 0000030415 00000 n 0000028379 00000 n 0000030589 00000 n 0000030646 00000 n 0000033085 00000 n 0000032854 00000 n 0000030795 00000 n 0000033028 00000 n 0000035205 00000 n 0000034974 00000 n 0000033177 00000 n 0000035148 00000 n 0000036888 00000 n 0000036714 00000 n 0000035297 00000 n 0000037902 00000 n 0000037727 00000 n 0000036980 00000 n 0000037846 00000 n 0000041216 00000 n 0000040928 00000 n 0000037983 00000 n 0000041102 00000 n 0000041159 00000 n 0000178261 00000 n 0000043992 00000 n 0000043817 00000 n 0000041320 00000 n 0000043936 00000 n 0000045711 00000 n 0000045536 00000 n 0000044073 00000 n 0000045655 00000 n 0000049160 00000 n 0000048985 00000 n 0000045792 00000 n 0000049104 00000 n 0000052014 00000 n 0000051375 00000 n 0000049253 00000 n 0000108229 00000 n 0000102853 00000 n 0000108065 00000 n 0000102104 00000 n 0000096022 00000 n 0000101943 00000 n 0000051530 00000 n 0000051676 00000 n 0000051818 00000 n 0000055041 00000 n 0000054867 00000 n 0000052133 00000 n 0000057780 00000 n 0000057606 00000 n 0000055184 00000 n 0000178378 00000 n 0000060183 00000 n 0000060009 00000 n 0000057911 00000 n 0000063546 00000 n 0000063315 00000 n 0000060314 00000 n 0000095266 00000 n 0000091170 00000 n 0000095104 00000 n 0000063489 00000 n 0000066929 00000 n 0000066698 00000 n 0000063691 00000 n 0000066872 00000 n 0000069077 00000 n 0000068902 00000 n 0000067062 00000 n 0000069021 00000 n 0000071312 00000 n 0000071137 00000 n 0000069158 00000 n 0000073291 00000 n 0000073116 00000 n 0000071431 00000 n 0000178495 00000 n 0000075427 00000 n 0000075252 00000 n 0000073410 00000 n 0000077879 00000 n 0000077704 00000 n 0000075546 00000 n 0000080337 00000 n 0000080162 00000 n 0000077984 00000 n 0000081195 00000 n 0000081020 00000 n 0000080456 00000 n 0000081139 00000 n 0000085059 00000 n 0000084547 00000 n 0000081276 00000 n 0000084888 00000 n 0000084945 00000 n 0000085002 00000 n 0000084686 00000 n 0000086795 00000 n 0000086563 00000 n 0000085176 00000 n 0000086738 00000 n 0000178612 00000 n 0000089724 00000 n 0000089492 00000 n 0000086912 00000 n 0000089667 00000 n 0000091090 00000 n 0000090858 00000 n 0000089841 00000 n 0000091033 00000 n 0000095732 00000 n 0000095514 00000 n 0000102654 00000 n 0000102358 00000 n 0000108701 00000 n 0000108481 00000 n 0000124323 00000 n 0000123957 00000 n 0000136515 00000 n 0000136175 00000 n 0000143686 00000 n 0000143404 00000 n 0000146051 00000 n 0000146027 00000 n 0000159626 00000 n 0000159236 00000 n 0000177486 00000 n 0000177079 00000 n 0000178705 00000 n 0000178806 00000 n 0000185280 00000 n 0000186165 00000 n 0000186204 00000 n 0000186242 00000 n 0000186430 00000 n trailer << /Size 481 /Root 479 0 R /Info 480 0 R >> startxref 186519 %%EOF jlint-3.0/manual.texi0000644000175000017500000017420710001266370015410 0ustar davehodaveho00000000000000'\input texinfo @c -*-texinfo-*- @setfilename readme.texi @c Jlint will check your Java code and find bugs, inconsistencies and synch- @c ronization problems by doing data flow analysis and building a lock graph. @c @c Original version (1.11) by Konstantin Knizhnik. @c Enhanced version (2.0) and a few bug fixes by Cyrille Artho. @c Version 2.1: FreeBSD port by Cyrille Artho, Visual C++ port by Maarten Breddels. @c @c This program is free software; you can redistribute it and/or @c modify it under the terms of the GNU General Public @c License as published by the Free Software Foundation; either @c version 2 of the License, or (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, @c but WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public @c License along with this library; if not, write to the Free @c Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, @c MA 02111-1307, USA. @dircategory Programming tools @direntry * jlint: (jlint). Java program checker @end direntry @titlepage @title Jlint manual @subtitle Java program checker @author Konstantin Knizhnik, Cyrille Artho @c The following two commands @c start the copyright page. @page 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. @end titlepage @contents @node Jlint, , , @top jlint This document described Jlint, a Java program checker that will check your Java code and find bugs, inconsistencies and synchronization problems by doing data flow analysis and building a lock graph. @menu * Introduction:: Introduction. * Bugs detected by AntiC:: Overview of AntiC. * Bugs detected by Jlint:: Overview of Jlint. * Command line options:: Usage of antic and jlint. * How to build:: Information about the Makefile. * Release notes:: Release notes. @end menu @node Introduction, Bugs detected by AntiC, , (dir) @chapter Introduction Jlint will check your Java code and find bugs, inconsistencies and synchronization problems by doing data flow analysis and building lock graph. Jlint consists of two separate programs performing syntax and semantic verification. As far as Java mostly inherits C/C++ syntax and so inherits most of the problems caused by C syntax, the idea was to create common syntax verifier for all C-family languages: C, C++, Objective C and Java. This program was named @strong{@emph{AntiC}}, because it fixes problems with C grammar, which can cause dangerous programmer's bugs, undetected by compiler. By using hand-written scanner and simple top-down parser, AntiC is able to detect such bugs as suspicious use of operators priorities, absence of break in switch code, wrong assumption about constructions bodies... Semantic verifier Jlint extracts information from Java class files. As far as Java class file has very well specified and simple format, it greatly simplifies Jlint in comparison with source level verifiers, because development of Java grammar parser is not a simple task (even through Java grammar is simpler and less ambiguous than C++ grammar). Also dealing only with class files, protect Jlint from further Java extensions (format of virtual byte instructions is more conservative). By using debugging information Jlint can associate reported messages with Java sources. Jlint performs local and global data flow analyses, calculating possible values of local variables and catching redundant and suspicious calculations. By performing global method invocation analysis, Jlint is able to detect invocation of method with possible "null" value of formal parameter and using of this parameter in method without check for "null". Jlint also builds lock dependency graph for classes dependencies and uses this graph to detect situations, which can cause @emph{deadlock} during multi-threaded program execution. Except deadlocks, Jlint is able to detect possible @emph{race condition} problem, when different threads can concurrently access the same variables. Certainly Jlint can't catch all synchronization problems, but at least it can do something, which can save you a lot of time, because synchronization bugs are the most dangerous bugs: non-deterministic, and not always reproducible. Unfortunately Java compiler can't help you with detecting synchronization bugs, may be Jlint can... Jlint uses smart approach to message reporting. All messages are grouped in categories, and it is possible to enable or disable reporting messages of specific category as well as concrete messages. Jlint can remember reported messages and do not report them once again when you run Jlint second time. This feature is implemented by means of history file. If you specify @var{-history} option, then before reporting a message, Jlint searches in this file if such message was already reported in the past. If so, then no message is reported and programmer will not have to spend time parsing the same messages several times. If message was not found in history file, it is reported and appended to history file to eliminate reporting of this message in future. Some messages refer to class/method name and are position independent, while some messages are reported for specific statement in method's code. Messages of second type will not be repeatedly reported only if method's source is not changed. @node Bugs detected by AntiC, Bugs detected by Jlint, Introduction, (dir) @chapter Bugs detected by AntiC Input of AntiC should be valid C/C++ or Java program with no syntax errors. If there are some syntax errors in the program, AntiC can detect some of them and produce error message, but it doesn't try to perform full syntax checking and can't recover after some errors. So in this chapter we discuss only the messages produced by AntiC for program without syntax errors. @node Bugs in tokens, Operator priorities, , Bugs detected by AntiC @section Bugs in tokens @subsection Octal digit expected Sequence of digits in string or character constant preceded by '\\' character contains non-octal digit: @example printf("\128"); @end example @subsection May be more than three octal digits are specified Sequence of digits in string or character constant preceded by '\\' character contains more than three digits: @example printf("\1234"); @end example @subsection May be more than four hex digits are specified for character constant String constant contains escape sequence for Unicode character, followed by character, which can be treated as hexadecimal digit: @example System.out.println("\uABCDE:"); @end example @subsection May be incorrect escape sequence Non-standard escape sequence is used in character or string constant: @example printf("\x"); @end example @subsection Trigraph sequence inside string Some C/C++ compilers still support trigraph sequences of ANSI C and replace the following sequences of characters ("??=", "??/", "??'", "??(", "??)", "??!", "??") with the characters ("#", "\", "^", "[", "]", "|", "{", "}") respectively. This feature may cause unexpected transformation of string constants: @example char* p = "???=undefined"; @end example @subsection Multi-byte character constants are not portable Multi-byte character constants are possible in C, but makes program non-portable. @example char ch = 'ab'; @end example @subsection May be 'l' is used instead of '1' at the end of integer constant It is difficult to distinct lower case letter 'l' and digit '1'. As far as letter 'l' can be used as long modifier at the end of integer constant, it can be mixed with digit. It is better to use upper-case 'L': @example long l = 0x111111l; @end example @node Operators priorities, Statement body, Bugs in tokens, Bugs detected by AntiC @section Operator priorities @subsection May be wrong assumption about operators precedence Several operators with non-intuitive clear precedence are used without explicit grouping by parentheses. Sometimes programmer's assumption about operators priorities is not true, and in any case enclosing such operations in parentheses can only increase readability of program. Below is list of some suspicious combinations of operators: @example x & y == z x && y & z x || y = z @end example @subsection May be wrong assumption about logical operators precedence Priority of logical AND operator is higher than priority of logical OR operator. So AND expression will be evaluated before OR expression even if OR precedes AND: @example x || y && z @end example @subsection May be wrong assumption about shift operator priority Priority of shift is smaller than of arithmetic operators but less than of bit manipulation operators. It can cause wrong assumption about operands grouping: @example x>>y - 1 x >> y&7 @end example @subsection May be '=' used instead of '==' Almost all C programmer did this bug, at least once in their life. It very easy to type '=' instead of '==' and not all C compilers can detect this situation. Moreover this bug is inherited by Java: the only restriction is that types of operands should be boolean: @example if (x = y) {} @end example @subsection May be skipped parentheses around assign operator Assign operators have one of the smallest priorities. So if you want to test result of assignment operation, you should enclose it in parentheses: @example if (x>>=1 != 0) {} @end example @subsection May be wrong assumption about bit operation priority Bit manipulation operators have smaller priority than compare operators. If you, for example, extracting bits using bit AND operator, do not forget to enclose it with parentheses, otherwise result of the expression will be far from your expectation: @example if (x == y & 1) {} @end example @node Statement body, , Operator priorities, Bugs detected by AntiC @section Statement body Almost all C statements can contain as its sub-part either single statement or block of statements (enclosed by braces). Unnoticed semicolon or wrong alignment can confuse programmer about real statement's body. And compiler can't produce any warnings, because it deals with stream of tokens, without information about code alignment. @subsection May be wrong assumption about loop body This message is produced if loop body is not enclosed in braces and indentation of the statement following the loop is bigger than of loop statement (i.e. it is shifted right): @example while (x != 0) x >>= 1; n += 1; return x; @end example @subsection May be wrong assumption about IF body This message is produced if IF body is not enclosed in braces and indentation of the statement following the IF construction is bigger than of IF statement itself (i.e. it is shifted right) or IF body is empty statement (';'): @example if (x > y); { int tmp = x; x = y; y = tmp; } if (x != 0) x = -x; sign = -1; sqr = x*x; @end example @subsection May be wrong assumption about ELSE branch association If there are no braces, then ELSE branch belongs to most inner IF. Sometimes programmers forget about it: @example if (rc != 0) if (perr) *perr = rc; else return Ok; @end example @subsection Suspicious SWITCH without body Switch statement body is not a block. With great probability it signals about some error in program: @example switch(j) { case 1: ... case 2: switch(ch); { case 'a': case 'b': ... } } @end example @subsection Suspicious CASE/DEFAULT Case is found in block not belonging to switch operator. Situations, where such possibility can be used are very rare: @example switch (n & 3) { do { default: *dst++ = 0; case 3: *dst++ = *drc++; case 2: *dst++ = *drc++; case 1: *dst++ = *drc++; } while ((n -= 4) > 0; } @end example @subsection Possible miss of BREAK before CASE/DEFAULT AntiC performs some kind of control flow analysis to detect situations, where control can be passed from one case branch to another (if programmer forget about BREAK statement). Sometimes it is necessary to merge several branches. AntiC doesn't produce this message in following cases: @enumerate @item Several cases point to the same statement: @example case '+': case '-': sign = 1; break; @end example @item Special @code{nobreak} macro is defined and used in switch statement: @example #define nobreak ... switch (cop) { case sub: sp[-1] = -sp[1]; nobreak; case add: sp[-2] += sp[-1]; break; ... } @end example @item Comment containing words "break" or "fall" is placed before the case: @example switch (x) { case do_some_extra_work: ... // fall thru case do_something: ... } @end example @end enumerate In all other cases message is produced when control can be passed from one switch branch to another: @example switch (action) { case op_remove: do_remove(); case op_insert: do_insert(); case op_edit: do_edit(); } @end example @node Bugs detected by Jlint, Command line options, Bugs detected by AntiC, (dir) @chapter Bugs detected by Jlint There are three main groups of messages produced by Jlint: @strong{@emph{synchronization, inheritance}} and @strong{@emph{data flow}}. These groups are distinguished by kind of analysis which is used to detect problems, reported in this messages. Each group is in turn divided into several categories, which contains one or more messages. Such scheme of message classification is used to support fine-grained selection of reported messages. Because only categories of message can be disabled, but not separate messages, a short shell script is supplied that will suppress certain warnings that are less important. It should be noted that it will ignore any race conditions for variables. This is because Jlint does not have any notion of "shared reading", so it usually produces too many warnings about such data races to be useful. For some projects, using Jlint's output unfiltered can still be useful. The shell script is as follows: @example @include jlint.sh @end example It is probably more easier to run Jlint using those filters, with @example cd jlint.sh @end example @node Synchronization, Inheritance, , Bugs detected by Jlint @section Synchronization Parallel execution of several threads of control requires some synchronization mechanism to avoid access conflicts to shared data. Java approach to synchronization is based on using object monitors, controlled by @code{synchronized} language construction. Monitor is always associated with object and prevents concurrent access to the object by using mutual exclusion strategy. Java also supports facilities for waiting and notification of some condition. Unfortunately, providing these synchronization primitives, Java compiler and virtual machine are not able to detect or prevent synchronization problems. Synchronization bugs are the most difficult bugs, because of non-deterministic behaviour of multi-threaded program. There are two main sources of synchronization problems: deadlocks and race conditions. Situation in which one or more threads mutually lock each other is called deadlock. Usually the reason of deadlock is inconsistent order of resource locking by different threads. In Java case resources are object monitors and deadlock can be caused by some sequence of method invocations. Let's look at the following example of multi-threaded database server: @example class DatabaseServer { public TransactionManager transMgr; public ClassManager classMgr; ... } class TransactionManager { protected DatabaseServer server; public synchronized void commitTransaction(ObjectDesc[] t_objects) { ... for (int i = 0; i < t_objects.length; i++) { ClassDesc desc = server.classMgr.getClassInfo(t_objects[i]); ... } ... } ... } class ClassManager { protected DatabaseServer server; public synchronized ClassDesc getClassInfo(ObjectDesc object) { ... } public synchronized void addClass(ClassDesc desc) { ObjectDesc t_objects; ... // Organized transaction to insert new class in database server.transMgr.commit_transaction(t_objects); } }; @end example If database server has one thread for each client and one client is committing transaction while another client adds new class to database, then deadlock can arise. Consider the following sequence: @enumerate @item Client A invokes method @code{TransactionManager.commitTransaction()}. While execution of this method monitor of TransactionManager object is locked. @item Client B invokes method @code{ClassManager.addClass()} and locks monitor of ClassManager object. @item Method @code{TransactionManager.commitTransaction()} tries to invoke method @code{ClassManager.getClassInfo()} but has to wait because this object is locked by another thread. @item Method @code{ClassManager.addClass()} tries to invoke method @code{TransactionManager.commitTransaction()} but has to wait because this object is locked by another thread. @end enumerate So we have deadlock and database server is halted and can't serve any client. The reason of this deadlock is loop in locking graph. Let's explain it less formally. We will construct oriented graph G of monitor lock relations. As far as locked resource are objects, so vertexes of this graph should be objects. But this analysis can't be done statically, because set of all object instances is not known at compile time. So the only kind of analysis, which Jlint is able to perform, is analysis of inter-class dependencies. So the vertexes of graph G will be classes. More precisely, each class C is represented by two vertexes: vertex C for class itself and vertex C' for metaclass. First kind of vertexes are used for dependencies caused by instance methods invocation, and second - by static methods. We will add edge (A,B) with mark "foo" to the graph if some synchronized method @code{foo()} of class B, can be invoked directly or indirectly from some synchronized method of class A for object other than @code{this}. For example for the following classes: @example class A { public synchronized void f1(B b) { b.g1(); f1(); f2(); } public void f2(B b) { b.g2(); } public static synchronized void f3() { B.g3(); } } class B { public static A ap; public static B bp; public synchronized void g1() { bp.g1(); } public synchronized void g2() { ap.f1(); } public static synchronized void g3() { g3(); } } @end example will add the following edges: @example g1 A --------> B, because of invocation of b.g1() from A.f1() g2 A --------> B, because of following call sequence: A.f1 -> A.f2 -> B.g2 g3 A' --------> B', because of invocation of b.g3() from A.f3() g1 B --------> B, loop edge because of recursive call for non-this object in B.g1(). f1 B --------> A, because of invocation of ap.f1() from B.g2() @end example Deadlock is possible only if there is loop in graph G. This condition is necessary, but not enough (presence of loop in graph G doesn't mean that program is not correct and deadlock can happen during it's execution). So using this criterion Jlint can produce messages about deadlock probability in case where deadlock is not possible. As far as task of finding all loops in the graph belongs to the NP class, no efficient algorithm for reporting all such loops exists at this moment. To do it work best and fast, Jlint uses restriction for number of loops, which pass through some graph vertex. There is another source of deadlock - execution of @code{wait()} method. This method unlocks monitor of current object and waits until some other thread notify it. Both methods @code{wait()} and @code{notify()} should be called with monitor locked. When thread is awaken from wait state, it tries to re-establish monitor lock and only after it can continue execution. The problem with @code{wait()} is that only one monitor is unlocked. If method executing @code{wait()} was invoked from synchronized method of some other object O, monitor of this object O will not be released by @code{wait}. If thread, which should notify sleeping thread, needs to invoke some synchronized method of object O, we will have deadlock: one thread is sleeping and thread, which can awoke it, waits until monitor will be unlocked. Jlint is able to detect situations when @code{wait()} method is called and more than one monitors are locked. But deadlock is not the only synchronization problem. Race condition or concurrent access to the same data is more serious problem. Let's look at the following class: @example class Account { protected int balance; public boolean get(int sum) { if (sum > balance) { balance -= sum; return true; } return false; } } @end example What will happen if several threads are trying to get money from the same account? For example account balance is $100. First thread tries to get $100 from the account - check is ok. Then, before first thread can update account balance, second thread tries to perform the same operation. Check is ok again! This situation is called @emph{race condition}, because result depends on "speed" of threads execution. How can Jlint detect such situations? First of all Jlint builds closure of all methods, which can be executed concurrently. The obvious candidates are synchronized methods and method @code{run} of classes implemented @code{Runnable} protocol or inherited from @code{Thread} class. Then all other methods, which can be invoked from these methods, are marked as concurrent. This process repeats until no more method can be added to concurrent closure. Jlint produces message about non-synchronized access only if all of the following conditions are true: @enumerate @item Method accessing field is marked as concurrent. @item Field is not declared as @code{volatile} or @code{final}. @item Field doesn't belong to @code{this} object of the method. @item It is not a field of just created object, which is accessed through local variable. @item Field can be accessed from methods of different classes. @end enumerate It is necessary to explain last two items. When object is created and initialized, usually only one thread can access this object through its local variables. So synchronization is not needed in this case. The explanation of item 5 is that not all objects, which are accessed by concurrent threads, need to be synchronized (and can't be declared as synchronized in some cases to avoid deadlocks). For example consider implementation of database set: @example class SetMember { public SetMember next; public SetMember prev; } class SetOwner { protected SetMember first; protected Setmember last; public synchronized void add_first(SetMember mbr) { if (first == null) { first = last = mbr; mbr.next = mbr.prev = null; } else { mbr.next = first; mbr.prev = null; first.prev = mbr; first = mbr; } } public synchronized void add_last(SetMember mbr) {...} public synchronized void remove(SetMember mbr) {...} }; @end example In this example @code{next} and @code{prev} components of class @code{SetMember} can be accessed only from synchronized methods of @code{SetOwner} class, so no access conflict is possible. Rule 5 was included to avoid reporting of messages in situations like this. Rules for detecting synchronization conflicts by Jlint are not finally defined, some of them can be refused or replaced, new candidates can be added. The main idea is to detect as much suspicious places as possible, while not producing confusing messages for correct code. @subsection Loop @var{id}: invocation of synchronized method @var{name} can cause deadlock @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{sync_loop} @end multitable Loop in class graph G @xref{Synchronization} is detected. One such message is produced for each edge of the loop. All loops are assigned unique identifier, so it is possible to distinguish messages for edges of one loop from another. @subsection Loop @var{LoopId/PathId}: invocation of method @var{name} forms the loop in class dependency graph @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{loop} @end multitable Reported invocation is used in call sequence from synchronized method of class A to synchronized method @code{foo()} of class B, so that edge (A,B) is in class graph G (@xref{Synchronization}). If method @code{foo()} is invoked directly, then only previous message (sync_loop) is reported. But if call sequence includes some other invocations (except invocation of @code{foo()}), then this message is produced for each element of call sequence. If several call paths exist for classes A, B and method @code{foo()}, then all of them (but not more than specified by @code{MaxShownPaths} parameter) are printed. @var{PathId} identifier is used to group messages for each path. @subsection Lock @var{a} is requested while holding lock @var{b}, with other thread holding @var{a} and requesting lock @var{b} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{lock} @end multitable This is one of the extensions for version 2: checking @code{synchronized} blocks. If, for one class, the locking scheme is such that it could lead to a cycle in the locking graph, this message is shown. @example public void foo() { synchronized (a) { synchronized (b) { } } } public void bar() { synchronized (b) { synchronized (a) { } } } @end example In this example, @code{a} and @code{b} are two objects that are used as locks and are shared between threads. If one thread call @code{foo} while another one calls @code{bar} simultaneously, a deadlock occurs. Jlint does not check whether @code{a} and @code{b} are actually used by several threads. However, if this were not the case, there is no point of using synchronizations on these variables. @subsection Method wait() can be invoked with monitor of other object locked @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{wait} @end multitable At the moment of @code{wait()} method invocations, more than one monitor objects are locked by the thread. As far as wait unlocks only one monitor, it can be a reason of deadlock. Successive messages of type @strong{wait_path} specify call sequence, which leads to this invocation. Monitors can be locked by invocation of a synchronized method or by explicit synchronized construction. Jlint handle both of the cases. The extended Jlint now checks @emph{which} locks are actually owned before issuing an error message. This error message now spans two lines, with the second line saying which locks are owned at that point. (Jlint will still count this as only one message when printing the total message count.) This should greatly facilitate debugging. @subsection Call sequence to method @var{name} can cause deadlock in wait() @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{wait_path} @end multitable By the sequence of such messages Jlint informs about possible invocation chain, which locks at least two object monitors and is terminated by method calling @code{wait()}. As far as @code{wait()} unlocks only one monitor and suspend thread, this can cause deadlock. @subsection Synchronized method @var{name} is overridden by non-synchronized method of derived class @var{name} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{race_condition} @item Message code: @tab @strong{nosync} @end multitable Method is declared as synchronized in base class, but is overridden in derived class by non-synchronized method. It is not a bug, but suspicious place, because if base method is declared as synchronized, then it is expected that this method can be called from concurrent threads and access some critical data. Usually the same is true for derived method, so disappearance of synchronized modifier looks suspiciously. @subsection Method @var{name} can be called from different threads and is not synchronized @multitable @columnfractions .5 .5 @item Message category: @tab @strong{race_condition} @item Message code: @tab @strong{concurrent_call} @end multitable Non-synchronized method is invoked from method marked as concurrent for object other than @code{this} (for instance methods) or for class, which is not base class of caller method class (for static methods). This message is reported only if invocation is not enclosed in synchronized construction and this method also can be invoked from methods of other classes. @subsection Field @var{name} of class @var{name} can be accessed from different threads and is not volatile @multitable @columnfractions .5 .5 @item Message category: @tab @strong{race_condition} @item Message code: @tab @strong{concurrent_access} @end multitable Field is accessed from method marked as concurrent. This message is produced only if: @enumerate @item Field belongs to the object other than @code{this} (for instance methods) or to classes which are not base for class of static method. @item Field is not component of object previously created by @code{new} and assigned to local variable. @item Field is not marked as volatile or final. @item Field can be accessed from methods of different classes. @end enumerate @subsection Method @var{name} implementing 'Runnable' interface is not synchronized @multitable @columnfractions .5 .5 @item Message category: @tab @strong{race_condition} @item Message code: @tab @strong{run_nosync} @end multitable Method @code{run()} of class implementing @code{Runnable} interface is not declared as synchronized. As far as different threads can be started for the same object implementing @code{Runnable} interface, method @code{run} can be executed concurrently and is first candidate for synchronization. @subsection Value of lock @var{name} is changed outside synchronization or constructor @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{loop_assign} @end multitable @example class Foo { Object a = new Object(); public void bar() { a = new Object(); synchronized (a) { } } } @end example The initialization of @var{a} (in the declaration, which will be moved into the constructor) is OK; however, changing the value outside any synchronization will make @var{a} useless as a locking variable. Therefore, Jlint will issue a warning for the assignment @code{a = new Object();} in @code{bar}. @subsection Value of lock @var{name} is changed while (potentially) owning it @multitable @columnfractions .5 .5 @item Message category: @tab @strong{deadlock} @item Message code: @tab @strong{loop_assign2} @end multitable @example class Quux { Object a = new Object(); public void foo() { synchronized (a) { bar(); } } public void bar() { a = new Object(); /* do something */ } } @end example In this example, the current thread still holds a lock on @var{a} when it re-initialized that variable (in method @code{bar}). This means that if another thread tried to obtain a lock on the new @var{a}, it can now proceed to do so, because the new value of @var{a} points to a different instance, which makes a synchronization on @var{a} ineffective. Probably this was not expected by the programmer, and this could lead to a potential race condition. The solution to this problem is to include another guard (in this case, @code{synchronized(this)}). @subsection Method @code{@var{name}.wait()} is called without synchronizing on @var{name} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{wait_nosync} @item Message code: @tab @strong{wait_nosync} @end multitable Method @code{wait()} or @code{notify()} is invoked from method, which is not declared as synchronized. It is not surely a bug, because monitor can be locked from another method, which directly or indirectly invokes current method. The improved Jlint (version 2) can also check @code{wait} calls to any object, and it will not report an error as long as the lock on @var{name} was obtained within the method that is currently being checked. This greatly reduces the amount of spurious warnings in that category. @node Inheritance, Data flow, Synchronization, Bugs detected by Jlint @section Inheritance This group contains messages, which are caused by problems with class inheritance: such as mismatch of methods profiles, components shadowing... As far as Jlint deals with Java class file and there is no information about line number in source file of class, field or method definition, Jlint can't show proper place in source file where class, field or method, which cause the problem, is located. In case of methods, Jlint points to the line corresponds to the first instruction of the method. And for classes and fields, Jlint always refers in message to the first line in source file. Jlint assign successive number (starting from 1) for all such message reported sequentially, because Emacs skips all messages, reported for the same line, when you go to next message. @subsection Method @var{name} is not overridden by method with the same name of derived class @var{name} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{not_overridden} @item Message code: @tab @strong{not_overridden} @end multitable Derived class contains the method with the same name as in base class, but profiles of these methods do not match. More precisely: message is reported when for some method of class A, exists method with the same name in derived class B, but there is no method with the same name in class B, which is compatible with definition of the method in class A (with the same number and types of parameters). Programmer writing this code may erroneously expect that method in derived class overrides method in base class and that virtual call of method of base class for object of derived class will cause execution method of the derived class. @subsection Component @var{name} in class @var{name} shadows one in base class @var{name} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{field_redefined} @item Message code: @tab @strong{field_redefined} @end multitable Field in derived class has the same name as field of some of base classes. It can cause some problems because this two fields points to different locations and methods of base class will access one field, while methods of derived class (and classes derived from it) will access another field. Sometimes it is what programmer expected, but in any case it will not improve readability of program. @subsection Local variable @var{name} shadows component of class @var{name} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{shadow_local} @item Message code: @tab @strong{shadow_local} @end multitable Local variable of method shadows class component with the same name. As far as it is common practice in constructors to use formal parameters with the same name as class components, Jlint detects situations, when class field is explicitly accessed by using @code{this} reference and doesn't report this message in this case: @example class A { public int a; public void f(int a) { this.a = a; // no message } public int g(int a) { return a; // message "shadow_local" will be reported } } @end example @subsection Method @code{finalize()} doesn't call @code{super.finalize()} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{super_finalize} @item Message code: @tab @strong{super_finalize} @end multitable As it is mentioned in book "The Java Programming Language" by Ken Arnold and James Gosling, calling of @code{super.finalize()} from @code{finalize()} is good practice of programming, even if base class doesn't define @code{finalize()} method. This makes class implementations less dependent from each other. @node Data flow, , Inheritance, Bugs detected by Jlint @section Data flow Jlint performs data flow analysis of Java byte code, calculating possible ranges of values of expressions and local variables. For integer types, Jlint calculates minimal and maximal value of expression and mask of possibly set bits. For object variables attribute @code{null/not_null} is calculated, selecting variables which value can be @code{null}. When value of expression is assigned to variable, these characteristics are copied to correspondent variable descriptor. Jlint handles control transfer instruction in special way: saving, modifying, merging or restoring context depending on type of instruction. Context in this consists of local variables states (minimal, maximal values and mask) and state of top of the stack (for handling ?: instruction). Initially all local integer variable are considered to have minimum and maximum properties equal to the range of correspondent type, and mask indicating that all bits in this range can be set. Object variables attribute initially is set to @code{not_null}. The same characteristics are always used for class components, because Jlint is not able to perform full data flow analysis (except checking for passing null value to formal parameter of methods). Table below summarizes actions performed by Jlint for handling control transfer instruction: @multitable @columnfractions .3 .3 .4 @item Instruction type @tab Correspondent Java construction @tab Action @item Forward conditional jump @tab IF statement @tab Save current context. Modify current context in assumption that condition is false (no jump). Modify saved context in assumption that condition is true (jump takes place) @item Forward unconditional jump @tab Start of loop, jump around ELSE branch of IF @tab Save current context @item Backward conditional jump @tab Loop statement condition @tab Modify context in assumption that condition is false (no jump) @item Backward unconditional jump @tab Infinite loop @tab Do nothing @item Label of forward jump @tab End of IF body or SWITCH case @tab If previous instruction is no-pass instruction (return, unconditional jump, throw exception) then restore saved context, otherwise merge current context with saved context (set minimum property of integer variable to minimum of this property value in current and saved contexts, maximum - to maximum of values in two contexts, and mask as join of masks in two context; for object variable - mark it as "may contain null" if it is marked so in one of contexts). If label corresponds to switch statement case, and switch expression is single local variable, then update properties of this variable by setting its minimum and maximum values and mask to value of case selector. @item Label of backward jump @tab Start of loop body @tab Reset properties of all variables modified between this label and backward jump instructions. Reset for integer variables means setting minimum property to minimum value of correspondent type, ... Reset for object variable clears mark "may contain null". @end multitable @subsection Method @var{name} can be invoked with NULL as @var{number} parameter and this parameter is used without check for null @multitable @columnfractions .5 .5 @item Message category: @tab @strong{null_reference} @item Message code: @tab @strong{null_param} @end multitable Formal parameter is used in the method without check for null (component of object is accessed or method of this object is invoked), while this method can be invoked with null as the value of this parameter (detected by global data flow analysis). Example: @example class Node { protected Node next; protected Node prev; public void link(Node after) { next = after.next; // Value of 'after' parameter can be null prev = after; after.next = next.prev = this; } } class Container { public void insert(String key) { Node after = find(key); if (after == null) { add(key); } Node n = new Node(key); n.link(after); // after can be null } } @end example @subsection Value of referenced variable @var{name} may be NULL @multitable @columnfractions .5 .5 @item Message category: @tab @strong{null_reference} @item Message code: @tab @strong{null_var} @end multitable Variable is used in the method without check for null. Jlint detects that referenced variable was previously assigned @code{null} value or was found to be @code{null} in one of control paths in the method. Jlint can produce this message in some situations, when value of variable can not actually be null: @example public int[] create1nVector(int n) { int[] v = null; if (n > 0) { v = new int[n]; } for (int i = 0; i < n; i++) { v[i] = i+1; // message will be reported } return v; } @end example @subsection NULL reference can be used @multitable @columnfractions .5 .5 @item Message category: @tab @strong{null_reference} @item Message code: @tab @strong{null_ptr} @end multitable Constant @code{null} is used as left operand of '.' operation: @example public void printMessage(String msg) { (msg != null ? new Message(msg) : null).Print(); } @end example @subsection Zero operand for operation @multitable @columnfractions .5 .5 @item Message category: @tab @strong{zero_operand} @item Message code: @tab @strong{zero_operand} @end multitable One of operands of binary operation is zero. This message can be produced for sequence of code like this: @example int x = 0; x += y; @end example @subsection Result of operation is always 0 @multitable @columnfractions .5 .5 @item Message category: @tab @strong{zero_result} @item Message code: @tab @strong{zero_result} @end multitable Jlint detects that for given operands, operation always produces zero result. This can be caused by overflow for arithmetic operations or by shifting all significant bits in shift operations or clearing all bits by bit AND operation. @subsection Shift with count @var{relation} than @var{integer} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{domain} @item Message code: @tab @strong{shift_count} @end multitable This message is reported when minimal value of shift count operand exceeds 31 for int type and 63 for long type or maximal value of shift count operand is less than 0: @example if (x > 32) { y >>= x; // Shift right with count greater than 32 } @end example @subsection Shift count range [@var{min,max}] is out of domain @multitable @columnfractions .5 .5 @item Message category: @tab @strong{domain} @item Message code: @tab @strong{shift_count} @end multitable Range of shift count operand is not within [0,31] for int type or [0,63] for long type. Jlint doesn't produce this message when distance between maximum and minimum values of shift count is greater than 255. So this message will not be reported if shift count is just variable of integer type: @example public int foo(int x, int y) { x >>= y; // no message x >>= 32 - (y & 31); // range of count is [1,32] } @end example @subsection Range of expression value has no intersection with @var{target} type domain @multitable @columnfractions .5 .5 @item Message category: @tab @strong{domain} @item Message code: @tab @strong{conversion} @end multitable Converted value is out of range of target type. This message can be reported not only for explicit conversions, but also for implicit conversions generated by compiler: @example int x = 100000; short s = x; // will cause this message @end example @subsection Data can be lost as a result of truncation to @var{type} @multitable @columnfractions .5 .5 @item Message category: @tab @strong{truncation} @item Message code: @tab @strong{truncation} @end multitable This message is reported when significant bits can be lost as a result of conversion from large integer type to smaller. Such conversions are always explicitly specified by programmer, so Jlint tries to reduce number of reported messages caused by data truncation. Example below shows when Jlint produces this message and when not: @example public void foo(int x, long y) { short s = (short)x; // no message char c = (char)x; // no message byte b = (byte)y; // no message b = (byte)(x & 0xff); // no message b = (byte)c; // no message c = (x & 0xffff); // no message x = (int)(y >>> 32); // no message b = (byte)(x >> 24); // truncation s = (int)(x & 0xffff00); // truncation x = (int)(y >>> 1); // truncation s = (short)c; // truncation } @end example @subsection May be type cast is not correctly applied @multitable @columnfractions .5 .5 @item Message category: @tab @strong{overflow} @item Message code: @tab @strong{overflow} @end multitable Result of operation, which has good chance to cause overflow (multiplication, left shift), is converted to long. As far as operation is performed with @code{int} operands, overflow can happen before conversion. Overflow can be avoided by conversion of one of operation operands to long, so operation will be performed with @code{long} operands. This message is produced not only for explicit type conversion done by programmer, but also for implicit type conversions performed by compiler: @example public long multiply(int a, int b) { return a*b; // operands are multiplied as integers // and then result will be converted to long } @end example @subsection Comparison always produces the same result @multitable @columnfractions .5 .5 @item Message category: @tab @strong{redundant} @item Message code: @tab @strong{same_result} @end multitable Using information about possible ranges of operands values, Jlint can make a conclusion, that logical expression is always evaluated to the same value (@code{true} or @code{false}): @example public void foo(int x) { if (x > 0) { ... if (x == 0) // always false { } } } @end example @subsection Compared operands can be equal only when both of them are 0 @multitable @columnfractions .5 .5 @item Message category: @tab @strong{redundant} @item Message code: @tab @strong{disjoint_mask} @end multitable By comparing operands masks, Jlint makes a conclusion that operands of @code{==} or @code{!=} operations can be equal only when both of them are zero: @example public boolean foo(int x, int y) { return ((x & 1) == y*2); // will be true only for x=y=0 } @end example @subsection Reminder always equal to the first operand @multitable @columnfractions .5 .5 @item Message category: @tab @strong{redundant} @item Message code: @tab @strong{redundant} @end multitable This message is produced for @code{%} operation when right operand is either greater either less than zero, and absolute value of left operand is less than absolute value of right operand. In this case @code{x % y == x} or @code{x % y == -x}. @subsection Comparison of short with char @multitable @columnfractions .5 .5 @item Message category: @tab @strong{short_char_cmp} @item Message code: @tab @strong{short_char_cmp} @end multitable Comparison of @code{short} operand with @code{char} operand. As far as @code{char} type is unsigned, and is converted to @code{int} by filling high half of the word with 0, and @code{short} type is signed and is converted to @code{int} using sign extension, then symbols in range @code{0x8000...0xFFFF} will not be considered equal in such comparison: @example boolean cmp() { short s = (short)0xabcd; char c = (char)s; return (c == s); // false } @end example @subsection Compare strings as object references @multitable @columnfractions .5 .5 @item Message category: @tab @strong{string_cmp} @item Message code: @tab @strong{string_cmp} @end multitable String operands are compared by @code{==} or @code{!=} operator. As far as @code{==} returns @code{true} only if operands point to the same object, so it can return false for two strings with same contents. The following function will return @code{false} in JDK1.1.5: @example public boolean bug() { return Integer.toString(1) == Integer.toString(1); } @end example @subsection Inequality comparison can be replaced with equality comparison @multitable @columnfractions .5 .5 @item Message category: @tab @strong{weak_cmp} @item Message code: @tab @strong{weak_cmp} @end multitable This message is produced in situations when ranges of compared operands intersect only in one point. So inequality comparison can be replaced with equality comparison. Such message can be caused by error in program, when programmer has wrong assumption about ranges of compared operands. But even if this inequality comparison is correct, replacing it with equality comparison can make code more clear: @example public void foo(char c, int i) { if (c <= 0) { // is it a bug ? if ((i & 1) > 0) { // can be replaced with (i & 1) != 0 ... } } } @end example @subsection Switch case constant @var{integer} can't be produced by switch expression @multitable @columnfractions .5 .5 @item Message category: @tab @strong{incomp_case} @item Message code: @tab @strong{incomp_case} @end multitable Constant in switch case is out of range of switch expression or has incompatible bit mask with switch expression: @example public void select(char ch, int i) { switch (ch) { case 1: case 2: case 3: ... case 256: // constant is out of range of switch expression } switch (i & ~1) { case 0: case 0xabcde: ... case 1: // switch expression is always even } } @end example @node Command line options, How to build, Bugs detected by Jlint, (dir) @chapter Command line options Both programs (AntiC and Jlint) accept list of files separated by spaces in command line. Wildcards are permitted. But unlike Unix, where wildcards are substituted by shell, in Windows wildcards are handled by program itself and wildcards only in file names (not in path directories) are allowed. @node AntiC command line options, Jlint command line options, , Command line options @section AntiC command line options AntiC supports only one command option: "-java". By default it consider input files as C/C++ source. There are very few differences (from AntiC point of view) between Java and C++. The differences are mostly with set of tokens and Unicode character constants. @node Jlint command line options, Jlint messages hierarchy, AntiC command line options, Command line options @section Jlint command line options Jlint option can be placed in any position in command line and takes effect for verification of all successive files in command line. Option always overrides previous occurrence of the same option. Some options specify parameters of global analysis, which is performed after loading of all files, so only the last occurrence of such options takes effect. Options are always compared ignoring letters case and @var{'_'} symbols. So the following two strings specify the same option: @var{-ShadowLocal} and @var{-shadow_local}. All Jlint options are prefixed by @var{'-'} or @var{'+'}. For options, which can be enabled or disabled, @var{'+'} means that option is enabled and @var{'-'} means that option is disabled. For options like @var{source} or @var{help} there is no difference between @var{'-'} and @var{'+'}. @table @samp @item -source @emph{@strong{path}} Specifies path to source files. It is necessary to specify this option when sources and class files are located in different directories. For example: @var{jlint -source /usr/local/jdk1.1.1/src /usr/local/jdk1.1.1/lib/classes.zip}. @item -history @emph{@strong{file}} Specifies history file. Jlint will not repeatedly report messages, which are present in history file. History file should be available for reading/writing and is appended by new messages after each Jlint execution. This messages will not be more reported in successive executions of Jlint (certainly if @var{-history} options is present and specifies the same history file). @item -max_shown_paths @emph{@strong{number}} Specifies number of different paths between two vertexes in class graph used for detecting possible deadlocks (@xref{Synchronization}). Default value of this parameter is 4. Increasing of this value can increase time of verification for complex programs. @item -help Output list of all options, including message categories. If option @var{+verbose} was previously specified, then list of all messages is also printed. @item (+-)verbose Switch on/off verbose mode. In verbose mode Jlint outputs more information about process of verification: names of verified files, warnings about absence of debugging information... @item (+-)@emph{@strong{message_category}} Enable or disable reporting of messages of specified category. It is possible to disable top level category and then enable some sub-categories within this category. And visa-versa it is possible to disable some specific categories within top-level category. It is also possible to disable concrete message codes within category. Table below describes full hierarchy of messages. By default all categories are enabled. @item (+-)all Enable/disable reporting of all messages. If @var{-all} is specified, it is possible to enable reporting of some specific categories of messages. For example to output only synchronization messages it is enough to specify "@var{-all +synchronization}". @item (+-)@emph{@strong{message_code}} Enable or disable reporting of concrete message. Message will be reported if its category is enabled and message code is enabled. If there is only one message code in the category, then names of the category and message code are the same. By default all messages are enabled. @end table @node Jlint messages hierarchy, , Jlint command line options, Command line options @section Jlint messages hierarchy @ifhtml
Top level category subcategory Message code
Synchronization deadlock syncLoop
loop
lock
wait
waitPath
raceCondition noSync
concurrentCall
concurrentAccess
runNoSync
lockAssign
lockAssign2
waitNoSyncwaitNoSync
Inheritance notOverriddennotOverridden
fieldRedefinedfieldRedefined
shadowLocalshadowLocal
superFinalizesuperFinalize
DataFlow nullReferencenullParam
nullVar
nullPtr
zeroOperandzeroOperand
zeroResultzeroResult
domainshiftCount
shiftRange
conversion
truncationtruncation
overflowoverflow
redundantsameResult
disjointMask
noEffect
shortCharCmpshortCharCmp
stringCmpstringCmp
weakCmpweakCmp
incompCaseincompCase

@end ifhtml @node How to build, Release notes, Command line options, (dir) @chapter How to build and use Jlint and AntiC Jlint is written on C++, using almost no operation system dependent code, so I hope it will not a problem to compile it on any system with C++ compiler. Current release contains makefile for Unix with gcc and for Windows with Microsoft Visual C++. In both cases it is enough to execute "make" to build "antic" and "jlint" programs. Distributive for Windows already includes executable files. To use Jlint you need to compile first you Java sources to byte code. As far as format of Java class is standard, you can use any available Java compiler. It is preferable to make compiler to include debug information in compiled classes (line table and local variables mapping). In this case Jlint messages will be more detailed. If your are using Sun @strong{javac} compiler, required option is @var{-g}. Most of compilers by default includes line table, but do not generate local variable table. For example free Java compiler @strong{guavac} can't generate it at all. Some compilers (like Sun's @strong{javac}) can't generate line table if optimization is switch on. If you specify @var{-verbose} option to Jlint, it will report when it can't find line or local variable table in the class file. Now Jlint and AntiC produce message in Emacs format: "@var{file:line: message text}". So it is possible to walk through these messages in Emacs if you start Jlint or AntiC as compiler. You can change prefix @var{MSG_LOCATION_PREFIX} (defined in @ifhtml types.hh @end ifhtml @file{types.hh}) from @var{"%0s:%1d: "} to one recognized by your favourite editor or IDE. All Jlint messages are gathered in file @ifhtml jlint.msg @end ifhtml @file{jlint.msg}, so you can easily change them (but recompilation is needed). AntiC also includes in the message position in the line. All AntiC messages are produced by function @code{message_at(int line, int coln, char* msg)}, defined in file @ifhtml antic.c @end ifhtml @file{antic.c}. You can change format of reported messages by modifying this function. @node Release notes, , How to build, (dir) @chapter Release notes Jlint 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. @c Original license (no restrictions): @c Jlint is shareware and is distributed with sources and @c without any restrictions. @c E-mail support is guaranteed. I will do my best to fix all reported bugs @c and extend Jlint functionality. Any suggestions and comments are welcome. @c I will be also very glad if somebody add some more stuff to Jlint or @c integrate it with some popular software development tools. @c Also modification of texts of reported messages @c in order to make them more clear (sorry, English is not my native language) or @c localization to some other languages are welcome. @c It can be also interesting to port Jlint to Java. @c @strong{Look for new version at my homepage}@strong{ | } @c @strong{E-mail me about bugs and problems} @ifhtml

Look for a new version

New Jlint page
Konstantin Knizhnik's homepage (alternative download location); original Jlint page

E-mail about bugs or problems

Cyrille Artho - about new synchronization features (synchronized blocks)
Konstantin Knizhnik - other questions
@end ifhtml @bye @c LocalWords: texi synch ronization MERCHANTABILITY texinfo readme @c LocalWords: setfilename uABCDE ch ab boolean tmp sqr rc perr perr Ok dst @c LocalWords: drc nobreak sp sp DatabaseServer TransactionManager transMgr @c LocalWords: ClassManager classMgr commitTransaction ObjectDesc ClassDesc @c LocalWords: desc getClassInfo addClass vertexes metaclass ap bp ok prev lt @c LocalWords: Runnable SetMember SetOwner Setmember mbr SetMemeber LoopId TR @c LocalWords: multitable columnfractions PathId MaxShownPaths nosync Quux hh @c LocalWords: param nVector ptr printMessage msg xff xffff cmp xabcd incomp @c LocalWords: toString toString xabcde Wildcards ShadowLocal samp max ifhtml @c LocalWords: subcategory ROWSPAN syncLoop waitPath raceCondition noSync IDE @c LocalWords: concurrentCall concurrentAccess runNoSync lockAssign noEffect @c LocalWords: waitNoSync waitNoSync notOverridden notOverridden shadowLocal @c LocalWords: fieldRedefined fieldRedefined shadowLocal disjointMask weakCmp @c LocalWords: shortCharCmp shortCharCmp stringCmp stringCmp weakCmp coln div @c LocalWords: incompCase incompCase english href br Knizhnik'sjlint-3.0/message_node.cc0000644000175000017500000000114307316371062016176 0ustar davehodaveho00000000000000#include "message_node.hh" message_node* message_node::hash_table[1023]; bool message_node::find(char* msg_text) { unsigned h = string_hash_function((byte*)msg_text) % items(hash_table); for (message_node* msg = hash_table[h]; msg != NULL; msg = msg->next) { if (strcmp(msg->text, msg_text) == 0) { return true; } } return false; } void message_node::add_to_hash(char* msg_text) { unsigned h = string_hash_function((byte*)msg_text) % items(hash_table); message_node* msg = new message_node(msg_text); msg->next = hash_table[h]; hash_table[h] = msg; } jlint-3.0/message_node.hh0000644000175000017500000000067607254476117016232 0ustar davehodaveho00000000000000#ifndef MESSAGE_NODE_HH #define MESSAGE_NODE_HH //#include //#include "types.hh" // included by functions.hh #include "functions.hh" class message_node { static message_node* hash_table[]; public: message_node* next; char* text; static bool find(char* msg); static void add_to_hash(char* msg); message_node(char* msg) { text = strdup(msg); next = NULL; } ~message_node() { delete[] text; } }; #endif jlint-3.0/method_desc.cc0000644000175000017500000027225407671343005016040 0ustar davehodaveho00000000000000#ifdef VISUAL_CPP #define snprintf _snprintf #endif #include "method_desc.hh" void print_call_sequence(callee_desc* callee, int loop_id, int path_id) { if (callee != NULL) { print_call_sequence((callee_desc*)callee->backtrace, loop_id, path_id); callee->message(msg_loop, (void*)loop_id, (void*)path_id, callee->method); } } int method_desc::demangle_method_name(char* buf) { char* dst = buf; const char* src = desc.as_asciz(); assert(*src++ == '('); dst += sprintf(dst, "%s.%s(", cls->name.as_asciz(), name.as_asciz()); int first_parameter = true; while (*src != ')') { if (!first_parameter) { *dst++ = ','; *dst++ = ' '; } first_parameter = false; int indirect = 0; while (*src == '[') { indirect += 1; src += 1; } switch (*src++) { case 'I': dst += sprintf(dst, "int"); break; case 'S': dst += sprintf(dst, "short"); break; case 'D': dst += sprintf(dst, "double"); break; case 'J': dst += sprintf(dst, "long"); break; case 'F': dst += sprintf(dst, "float"); break; case 'B': dst += sprintf(dst, "byte"); break; case 'C': dst += sprintf(dst, "char"); break; case 'Z': dst += sprintf(dst, "boolean"); break; case 'L': while (*src != ';') { if (*src == '/') *dst++ = '.'; else *dst++ = *src; src += 1; } src += 1; } while (indirect != 0) { *dst++ = '['; *dst++ = ']'; indirect -= 1; } } *dst++ = ')'; *dst = 0; return dst - buf; } void method_desc::check_invocations() { for (int i = 0; i < 32; i++) { if (null_parameter_mask & unchecked_use_mask & (1 << i)) { message_at(msg_null_param, cls->source_file, first_line, this, i); } } } bool method_desc::build_call_graph(method_desc* caller, callee_desc* callee, int caller_attr) { callee->backtrace = caller; for (overridden_method* ovr = overridden; ovr != NULL; ovr = ovr->next) { ovr->method->build_call_graph(caller, callee, caller_attr); } if ((attr & m_synchronized) || (attr & m_sync_block)) { if (!(caller_attr & callee_desc::i_self)) { #ifdef DUMP_EDGES char buf[2][MAX_MSG_LENGTH]; caller->demangle_method_name(buf[0]); demangle_method_name(buf[1]); printf("Call graph edge %s -> %s\n", buf[0], buf[1]); #endif graph_edge* edge = new graph_edge(vertex, caller, callee); assert(caller->vertex != NULL); caller->vertex->attach(edge); } return true; } else if (!(attr & m_deadlock_free)) { bool can_cause_deadlock = false; for (callee = callees; callee != NULL; callee = callee->next) { if (callee->backtrace != caller) { can_cause_deadlock |= callee->method->build_call_graph(caller, callee, caller_attr&callee->attr); } } if (!can_cause_deadlock) { attr |= m_deadlock_free; } return can_cause_deadlock; } return false; } void method_desc::check_synchronization() { if (attr & m_concurrent) { for (callee_desc* callee = callees; callee != NULL; callee = callee->next) { if (!(callee->method->attr & (m_serialized|m_synchronized)) && !(callee->method->cls->attr & class_desc::cl_system) && !(callee->attr & (callee_desc::i_self |callee_desc::i_synchronized)) && (strstr(callee->method->name.as_asciz(), "init>") == NULL) ) { callee->message(msg_concurrent_call, callee->method); } } for (access_desc* accessor = accessors; accessor != NULL; accessor = accessor->next) { if (!(accessor->field->attr & (field_desc::f_volatile|field_desc::f_final |field_desc::f_serialized)) && !(accessor->field->cls->attr & class_desc::cl_system) && !(accessor->attr&(access_desc::a_new|access_desc::a_self))) { accessor->message(msg_concurrent_access, &accessor->field->name,accessor->field->cls); } } } } void method_desc::find_access_dependencies() { for (callee_desc* callee = callees; callee != NULL; callee = callee->next){ class_desc* caller_class = callee->method->accessor; if (caller_class == NULL) { callee->method->accessor = cls; } else if (!cls->in_relationship_with(caller_class)) { // Method is called from two unrelated classes, so if this // two methods are exeuted concurretly we will have access conflict callee->method->attr &= ~m_serialized; } if ((!(attr & m_static) && (callee->method->attr & m_static)) || !(attr & m_concurrent)) { // // If caller is instance method and callee - static method // of the class, or caller is not included in concurrent closure // (and so it can be exeuted in main thread of control), then // any invocation of callee method from concurrent thread can // cause concurrent execution of this method. If no method // from concurrent closure invoke this method, then // "m_serialized" attribute will not checked at all, otherwise // message about concurrent invocation of non-synchronized method // will be reported. // callee->method->attr &= ~m_serialized; } } for (access_desc* accessor = accessors; accessor != NULL; accessor = accessor->next) { class_desc* accessor_class = accessor->field->accessor; if (accessor_class == NULL) { accessor->field->accessor = cls; } else if (!cls->in_relationship_with(accessor_class)) { accessor->field->attr &= ~field_desc::f_serialized; } if ((attr & m_static) && (accessor->field->attr & field_desc::f_static) && cls->isa(accessor->field->cls)) { accessor->attr |= access_desc::a_self; } if ((!(attr & m_static) && (accessor->field->attr & field_desc::f_static)) || !(attr & m_concurrent)) { accessor->field->attr &= ~field_desc::f_serialized; } } } void method_desc::build_call_graph() { if (attr & m_synchronized) { for (callee_desc* callee=callees; callee != NULL; callee=callee->next){ callee->method->build_call_graph(this, callee, callee->attr); } } } int method_desc::print_call_path_to(callee_desc* target, int loop_id, int path_id, int caller_attr, callee_desc* prev) { if (attr & (m_deadlock_free|m_visited)) { return path_id; } attr |= m_visited; if (prev != NULL) { for (overridden_method* ovr = overridden; ovr != NULL && path_id < max_shown_paths; ovr = ovr->next) { path_id = ovr->method->print_call_path_to(target, loop_id, path_id, caller_attr, prev); } } for (callee_desc* callee = callees; callee != NULL && path_id < max_shown_paths; callee = callee->next) { int callee_attr = callee->attr & caller_attr; if (callee->method->attr & m_synchronized) { if (callee == target && !(callee_attr & callee_desc::i_self)) { print_call_sequence(prev, loop_id, ++path_id); } } else { callee->backtrace = prev; path_id = callee->method->print_call_path_to(target, loop_id, path_id, callee_attr, callee); } } attr &= ~m_visited; return path_id; } void method_desc::add_to_concurrent_closure(callee_desc* caller, int caller_attr, int depth) { for (overridden_method* ovr=overridden; ovr != NULL; ovr=ovr->next) { add_to_concurrent_closure(caller, caller_attr, depth); } if (attr & m_synchronized) { if ((caller_attr & (callee_desc::i_synchronized|callee_desc::i_self)) == callee_desc::i_synchronized && (attr & m_wait)) { int callee_attr = callee_desc::i_self|callee_desc::i_wait_deadlock; callee_desc* up = caller; while (true) { callee_attr &= up->attr; if (up->backtrace == NULL || (!(callee_attr & callee_desc::i_self) && ((up->attr & callee_desc::i_synchronized) || (((callee_desc*)up->backtrace)->method->attr & m_synchronized)))) { break; } up = (callee_desc*)up->backtrace; } // Check if this pass was already found if (!(callee_attr & callee_desc::i_wait_deadlock)) { message_at(msg_wait, cls->source_file, wait_line, this); callee_desc* bt = caller; while (true) { bt->message(msg_wait_path, bt->method); bt->attr |= callee_desc::i_wait_deadlock; if (bt == up) break; bt = (callee_desc*)bt->backtrace; } } } attr |= m_concurrent; if ((caller_attr & (callee_desc::i_synchronized|callee_desc::i_self)) == callee_desc::i_synchronized && !(attr & m_visited)) { attr |= m_visited; for (callee_desc* callee = callees; callee != NULL; callee = callee->next) { if (callee->attr & callee_desc::i_self) { callee->backtrace = caller; callee->method->add_to_concurrent_closure (callee, callee_desc::i_synchronized, 0); } } attr &= ~m_visited; } } else if (!(attr & m_visited) && !((depth != 0 || (caller_attr & callee_desc::i_self)) && (attr & m_concurrent))) { if (attr & m_concurrent) { depth += 1; } attr |= m_visited|m_concurrent; for (callee_desc* callee=callees; callee != NULL; callee=callee->next){ int callee_attr = caller_attr; if (callee->attr & callee_desc::i_synchronized) { callee_attr = callee_desc::i_synchronized; } else { callee_attr &= callee->attr|~callee_desc::i_self; } callee->backtrace = caller; callee->method->add_to_concurrent_closure(callee, callee_attr, depth); } attr &= ~m_visited; } } void method_desc::build_concurrent_closure() { // // Check if "run" method of class implementing Runnable interface // is synchronized and mark this method as concurrent. // int caller_attr = 0; attr |= m_visited; if (attr & m_synchronized) { caller_attr = callee_desc::i_synchronized; } else if (name == "run" && (cls->implements("java/lang/Runnable") || cls->isa("java/lang/Thread"))) { if (cls->implements("java/lang/Runnable")) { message_at(msg_run_nosync, cls->source_file, first_line, this); } } else { for (callee_desc* callee=callees; callee != NULL; callee=callee->next){ if (callee->attr & callee_desc::i_synchronized) { callee->backtrace = NULL; callee->method-> add_to_concurrent_closure(callee, callee_desc::i_synchronized, 0); } } attr &= ~m_visited; return; } for (callee_desc* callee=callees; callee != NULL; callee=callee->next) { int callee_attr = callee->attr; if (callee_attr & callee_desc::i_synchronized) { // Synchronized construction was used. Clear "i_self" bit, // because two monitors are already locked callee_attr &= ~callee_desc::i_self; } callee->backtrace = NULL; callee->method->add_to_concurrent_closure(callee, callee_attr|caller_attr, 0); } attr &= ~m_visited; } void method_desc::calculate_attributes() { vertex = (attr & m_static) ? cls->metaclass_vertex : cls->class_vertex; // // Find out all "self" static invocations // for (callee_desc* callee = callees; callee != NULL; callee=callee->next) { if ((attr & callee->method->attr & m_static) && cls->isa(callee->method->cls)) { callee->attr |= callee_desc::i_self; } } } int method_desc::get_line_number(int pc) { while (line_table[pc] == 0 && pc > 0) { pc -= 1; } return line_table[pc]; } void method_desc::message(int code, int pc, ...) { va_list ap; va_start(ap, pc); #ifdef PRINT_PC printf("In %s.%s pc=%d\n", cls->name.as_asciz(), name.as_asciz(), pc); #endif format_message(code, cls->source_file, get_line_number(pc), ap); va_end(ap); } void method_desc::check_variable_for_null(int pc, vbm_operand* sp) { if (!(sp->mask & var_desc::vs_not_null)) { if (sp->index >= 0) { message(msg_null_var, pc, &vars[sp->index].name); } else { message(msg_null_ptr, pc); } } else if ((unsigned)sp->index < 32 && (sp->mask & var_desc::vs_not_null) != var_desc::vs_not_null) { // formal parameter was not assigned value // and was not checked for NULL if (null_parameter_mask & (1 << sp->index)) { message(msg_null_param, pc, this, sp->index); } else { unchecked_use_mask |= 1 << sp->index; } } } void method_desc::check_array_index(int pc, vbm_operand* sp) { check_variable_for_null(pc, sp); if (sp[1].min < 0) { if (sp[1].max < 0) { message(msg_bad_index, pc, sp[1].min, sp[1].max); } else if (sp[1].min > -128) { message(msg_maybe_bad_index, pc, sp[1].min, sp[1].max); } } if (sp[1].max >= sp->max && sp->max != MAX_ARRAY_LENGTH) { if (sp[1].min >= sp->max) { message(msg_bad_index, pc, sp[1].min, sp[1].max); } else if (sp[1].max - sp->max < 127) { message(msg_maybe_bad_index, pc, sp[1].min, sp[1].max); } } } void method_desc::basic_blocks_analysis() { byte* pc = code; byte* end = pc + code_length; int i; var_store_count = n_vars > 0 ? new int[n_vars] : NULL; for (i = n_vars; --i >= 0;) { vars[i].type = tp_void; vars[i].name = utf_string("???"); vars[i].min = 0; vars[i].max = MAX_ARRAY_LENGTH; var_store_count[i] = 0; } while (pc != end) { int addr = pc - code; int offs; switch (*pc) { case jsr: case ifeq: case ifne: case iflt: case ifge: case ifgt: case ifle: case if_icmpeq: case if_icmpne: case if_icmplt: case if_icmpge: case if_icmpgt: case if_icmple: case if_acmpeq: case if_acmpne: case ifnull: case ifnonnull: case goto_near: offs = (short)unpack2(pc+1); if (offs < 0) { // backward jump new ctx_reset(&context[addr+offs], var_store_count, n_vars); new ctx_split(&context[addr], ctx_split::jmp_backward); } else if (offs > 0) { // forward jump new ctx_merge(&context[addr+offs], new ctx_split(&context[addr], ctx_split::jmp_forward)); } pc += 3; break; case jsr_w: case goto_w: offs = unpack4(pc+1); if (offs < 0) { // backward jump new ctx_reset(&context[addr+offs], var_store_count, n_vars); } else if (offs > 0) { // forward jump new ctx_merge(&context[addr+offs], new ctx_split(&context[addr], ctx_split::jmp_forward)); } pc += 5; break; case tableswitch: { pc += 4 - (addr & 3); offs = unpack4(pc); // default label int low = unpack4(pc+4); int high = unpack4(pc+8); int n_forward_jumps = 0; pc += 12; ctx_split* select = new ctx_split(&context[addr]); if (offs < 0) { new ctx_reset(&context[addr+offs], var_store_count, n_vars); } else if (offs > 0) { new ctx_merge(&context[addr+offs], select); n_forward_jumps += 1; } while (low <= high) { offs = unpack4(pc); if (offs < 0) { new ctx_reset(&context[addr+offs], var_store_count, n_vars); } else if (offs > 0) { new ctx_merge(&context[addr+offs], select, low); n_forward_jumps += 1; } pc += 4; low += 1; } select->n_branches = n_forward_jumps; } break; case lookupswitch: { pc += 4 - (addr & 3); offs = unpack4(pc); // default label int n_pairs = unpack4(pc+4); int n_forward_jumps = 0; pc += 8; ctx_split* select = new ctx_split(&context[addr]); if (offs < 0) { new ctx_reset(&context[addr+offs], var_store_count, n_vars); } else if (offs > 0) { new ctx_merge(&context[addr+offs], select); n_forward_jumps += 1; } while (--n_pairs >= 0) { offs = unpack4(pc+4); if (offs < 0) { new ctx_reset(&context[addr+offs], var_store_count, n_vars); } else if (offs > 0) { new ctx_merge(&context[addr+offs], select, unpack4(pc)); n_forward_jumps += 1; } pc += 8; } select->n_branches = n_forward_jumps; } break; case istore: case lstore: case astore: pc += 1; var_store_count[*pc++] += 1; break; case istore_0: case istore_1: case istore_2: case istore_3: var_store_count[*pc++ - istore_0] += 1; break; case lstore_0: case lstore_1: case lstore_2: case lstore_3: var_store_count[*pc++ - lstore_0] += 1; break; case astore_0: case astore_1: case astore_2: case astore_3: var_store_count[*pc++ - astore_0] += 1; break; case iinc: var_store_count[pc[1]] += 1; pc += 3; break; case wide: pc += 1; switch (*pc++) { case istore: case lstore: case astore: var_store_count[unpack2(pc)] += 1; break; case iinc: var_store_count[unpack2(pc)] += 1; pc += 2; } pc += 2; break; default: pc += vbm_instruction_length[*pc]; } } for (i = n_vars; --i >= 0;) { var_store_count[i] = 0; } } void method_desc::parse_code(constant** constant_pool, const field_desc* is_this) { #ifdef DEBUG printf("Method %s\n", name.as_asciz()); #endif const int indirect = 0x100; byte* pc = code; byte* end = pc + code_length; const int max_stack_size = 256; vbm_operand stack[max_stack_size+2]; int i; for (i = 0; i < max_stack_size+2; i++) { stack[i].equals = NULL; } vbm_operand* stack_bottom = stack+2; // avoid checks for non-empty stack vbm_operand* sp = stack_bottom; local_context* ctx; byte prev_cop = nop; bool super_finalize = false; #ifdef INT8_DEFINED int8 left_min; int8 left_max; int8 left_mask; int8 right_min; int8 right_max; int8 right_mask; #define LOAD_INT8_OPERANDS() \ left_min = LOAD_INT8(sp-4, min);\ left_max = LOAD_INT8(sp-4, max);\ left_mask = LOAD_INT8(sp-4, mask);\ right_min = LOAD_INT8(sp-2, min);\ right_max = LOAD_INT8(sp-2, max);\ right_mask = LOAD_INT8(sp-2, mask) #define LOAD_INT8_OPERAND() \ left_min = LOAD_INT8(sp-2, min);\ left_max = LOAD_INT8(sp-2, max);\ left_mask = LOAD_INT8(sp-2, mask) #endif in_monitor = false; stack[0].type = stack[1].type = tp_void; cls->locks.clear(); basic_blocks_analysis(); for (i = 0; i < 32; i++) { if (null_parameter_mask & (1 << i)) { assert(i < n_vars); vars[i].mask = 0; vars[i].min = 0; vars[i].max = MAX_ARRAY_LENGTH; if (vars[i].type == tp_void) { vars[i].type = tp_object; } } } if (!(attr & m_static)) { vars[0].type = tp_self; vars[0].mask = var_desc::vs_not_null; vars[0].min = 0; vars[0].max = MAX_ARRAY_LENGTH; vars[0].equals = is_this; } if (attr & m_synchronized) { // add "this" to lock set if needed cls->locks.acquire(is_this); locksAtEntry.acquire(is_this); } while (pc < end) { int addr = pc - code; byte cop = *pc++; #ifdef DUMP_BYTE_CODES printf("%s: min=%d, max=%d, mask=0x%x\n", vbm_instruction_mnemonic[cop], sp[-1].min, sp[-1].max, sp[-1].mask); #endif for (ctx = context[addr]; ctx != NULL; ctx = ctx->next) { sp = ctx->transfer(this, sp, cop, prev_cop); } switch (cop) { case nop: break; case aconst_null: sp->type = tp_object; sp->mask = 0; sp->min = sp->max = 0; sp->index = NO_ASSOC_VAR; sp += 1; break; case iconst_m1: case iconst_0: case iconst_1: case iconst_2: case iconst_3: case iconst_4: case iconst_5: sp->type = tp_byte; sp->mask = sp->min = sp->max = cop - iconst_0; sp->index = NO_ASSOC_VAR; sp += 1; break; case fconst_0: case fconst_1: case fconst_2: sp->type = tp_float; sp->index = NO_ASSOC_VAR; sp += 1; break; case lconst_0: case lconst_1: sp[0].type = sp[1].type = tp_long; sp[0].min = sp[0].max = sp[0].mask = 0; sp[1].min = sp[1].max = sp[1].mask = cop - lconst_0; sp[0].index = sp[1].index = NO_ASSOC_VAR; sp += 2; break; case dconst_0: case dconst_1: sp[0].type = sp[1].type = tp_double; sp[0].index = sp[1].index = NO_ASSOC_VAR; sp += 2; break; case bipush: sp->type = tp_int; sp->mask = sp->min = sp->max = (signed char)*pc++; sp->index = NO_ASSOC_VAR; sp += 1; break; case sipush: sp->type = tp_int; sp->mask = sp->min = sp->max = (short)unpack2(pc); sp->index = NO_ASSOC_VAR; pc += 2; sp += 1; break; case ldc: { constant* cp = constant_pool[*pc++]; sp->type = cp->type(); if (sp->type == tp_int) { sp->mask = sp->min = sp->max = ((const_int*)cp)->value; } else { if (sp->type == tp_string) { sp->min = sp->max = ((const_string*)cp)->length(); } else { sp->min = 0; sp->max = MAX_ARRAY_LENGTH; } sp->mask = var_desc::vs_not_null; } sp->index = NO_ASSOC_VAR; sp->equals = is_const; sp += 1; } break; case ldc_w: { constant* cp = constant_pool[unpack2(pc)]; sp->type = cp->type(); if (sp->type == tp_int) { sp->mask = sp->min = sp->max = ((const_int*)cp)->value; } else { if (sp->type == tp_string) { sp->min = sp->max = ((const_string*)cp)->length(); } else { sp->min = 0; sp->max = MAX_ARRAY_LENGTH; } sp->mask = var_desc::vs_not_null; } sp->index = NO_ASSOC_VAR; pc += 2; sp += 1; } break; case ldc2_w: { const_long* cp = (const_long*)constant_pool[unpack2(pc)]; sp->type = tp_long; sp->index = NO_ASSOC_VAR; sp->mask = sp->min = sp->max = cp->value.high; sp += 1; sp->type = tp_long; sp->index = NO_ASSOC_VAR; sp->mask = sp->min = sp->max = cp->value.low; sp += 1; pc += 2; } break; case iload: if (vars[*pc].type == tp_void) { vars[*pc].type = tp_int; vars[*pc].min = ranges[tp_int].min; vars[*pc].max = ranges[tp_int].max; vars[*pc].mask = ALL_BITS; } sp->type = vars[*pc].type; sp->min = vars[*pc].min; sp->max = vars[*pc].max; sp->mask = vars[*pc].mask; sp->index = *pc++; sp += 1; break; case aload: if (vars[*pc].type == tp_void) { vars[*pc].type = tp_object; vars[*pc].min = 0; vars[*pc].max = MAX_ARRAY_LENGTH; vars[*pc].mask = var_desc::vs_unknown; } sp->type = vars[*pc].type; sp->mask = vars[*pc].mask; sp->min = vars[*pc].min; sp->max = vars[*pc].max; sp->equals = vars[*pc].equals; #ifdef DUMP_STACK printf(" := %x %s\n", (int)sp->equals, sp->equals == NULL ? "" : sp->equals->name.as_asciz() ); #endif sp->index = *pc++; sp += 1; break; case lload: { int index = *pc++; if (vars[index].type == tp_void) { vars[index].type = tp_long; vars[index].min = 0x80000000; vars[index].max = 0x7fffffff; vars[index].mask = 0xffffffff; vars[index+1].min = 0x00000000; vars[index+1].max = 0xffffffff; vars[index+1].mask = 0xffffffff; } sp->type = tp_long; sp->min = vars[index].min; sp->max = vars[index].max; sp->mask = vars[index].mask; sp->index = index; sp += 1; index += 1; sp->type = tp_long; sp->min = vars[index].min; sp->max = vars[index].max; sp->mask = vars[index].mask; sp += 1; } break; case dload: sp[0].type = sp[1].type = tp_double; sp[0].index = sp[1].index = NO_ASSOC_VAR; sp += 2; pc += 1; break; case fload: sp->type = tp_float; sp->index = NO_ASSOC_VAR; sp += 1; pc += 1; break; case iload_0: case iload_1: case iload_2: case iload_3: { var_desc* var = &vars[cop - iload_0]; if (var->type == tp_void) { var->type = tp_int; var->min = ranges[tp_int].min; var->max = ranges[tp_int].max; var->mask = ALL_BITS; } sp->type = var->type; sp->min = var->min; sp->max = var->max; sp->mask = var->mask; sp->index = cop - iload_0; sp += 1; } break; case lload_0: case lload_1: case lload_2: case lload_3: { int index = cop - lload_0; if (vars[index].type == tp_void) { vars[index].type = tp_long; vars[index].min = 0x80000000; vars[index].max = 0x7fffffff; vars[index].mask = 0xffffffff; vars[index+1].min = 0x00000000; vars[index+1].max = 0xffffffff; vars[index+1].mask = 0xffffffff; } sp->type = tp_long; sp->min = vars[index].min; sp->max = vars[index].max; sp->mask = vars[index].mask; sp->index = index; sp += 1; index += 1; sp->type = tp_long; sp->min = vars[index].min; sp->max = vars[index].max; sp->mask = vars[index].mask; sp += 1; } break; case dload_0: case dload_1: case dload_2: case dload_3: sp[0].type = sp[1].type = tp_double; sp[0].index = sp[1].index = NO_ASSOC_VAR; sp += 2; break; case fload_0: case fload_1: case fload_2: case fload_3: sp->type = tp_float; sp->index = NO_ASSOC_VAR; sp += 1; break; case aload_0: case aload_1: case aload_2: case aload_3: { var_desc* var = &vars[cop - aload_0]; if (var->type == tp_void) { if (cop == aload_0 && !(attr & m_static)) { var->type = tp_self; var->mask = var_desc::vs_not_null; } else { var->type = tp_object; var->mask = var_desc::vs_unknown; } sp->min = 0; sp->max = MAX_ARRAY_LENGTH; } sp->type = var->type; sp->mask = var->mask; sp->min = var->min; sp->max = var->max; sp->index = cop - aload_0; sp->equals = vars[cop - aload_0].equals; #ifdef DUMP_STACK printf(" := %x %s\n", (int)sp->equals, sp->equals == NULL ? "" : sp->equals->name.as_asciz() ); #endif sp += 1; } break; case iaload: case faload: case aaload: case baload: case caload: case saload: sp -= 2; check_array_index(addr, sp); if (IS_ARRAY_TYPE(sp->type)) { sp->type -= indirect; } else { sp->type = (cop == aaload) ? tp_object : (cop == baload) ? tp_byte : (cop == caload) ? tp_char : (cop == iaload) ? tp_int : (cop == saload) ? tp_short : tp_float; } if (IS_INT_TYPE(sp->type)) { sp->min = ranges[sp->type].min; sp->max = ranges[sp->type].max; sp->mask = sp->min|sp->max; } else { sp->min = 0; sp->max = MAX_ARRAY_LENGTH; sp->mask = var_desc::vs_unknown; } sp->index = NO_ASSOC_VAR; sp += 1; break; case laload: check_array_index(addr, sp-2); sp[-1].type = sp[-2].type = tp_long; sp[-1].index = sp[-2].index = NO_ASSOC_VAR; sp[-2].min = 0x80000000; sp[-2].max = 0x7fffffff; sp[-2].mask = 0xffffffff; sp[-1].min = 0x00000000; sp[-1].max = 0xffffffff; sp[-1].mask = 0xffffffff; break; case daload: check_array_index(addr, sp-2); sp[-1].type = sp[-2].type = tp_double; sp[-1].index = sp[-2].index = NO_ASSOC_VAR; break; case istore: { var_desc* var = &vars[*pc]; sp -= 1; var->max = sp->max; var->min = sp->min; var->mask = sp->mask; var_store_count[*pc] += 1; if (var->type == tp_void) { var->type = tp_int; } pc += 1; } break; case astore: sp -= 1; vars[*pc].mask = sp->mask; vars[*pc].min = sp->min; vars[*pc].max = sp->max; var_store_count[*pc] += 1; if (vars[*pc].type == tp_void) { vars[*pc].type = tp_object; } vars[*pc].equals = sp->equals; #ifdef DUMP_STACK for (i = 0; i <= *pc; i++) { printf(" := %x %s\n", i, (int)vars[i].equals, vars[i].equals == NULL ? "" : vars[i].equals->name.as_asciz() ); } #endif pc += 1; break; case fstore: pc += 1; sp -= 1; break; case lstore: { int index = *pc++; sp -= 2; vars[index].type = tp_long; vars[index].max = sp[0].max; vars[index].min = sp[0].min; vars[index].mask = sp[0].mask; vars[index+1].max = sp[1].max; vars[index+1].min = sp[1].min; vars[index+1].mask = sp[1].mask; var_store_count[index] += 1; } break; case dstore: pc += 1; sp -= 2; break; case istore_0: case istore_1: case istore_2: case istore_3: { var_desc* var = &vars[cop - istore_0]; sp -= 1; var->max = sp->max; var->min = sp->min; var->mask = sp->mask; if (var->type == tp_void) { var->type = tp_int; } var_store_count[cop - istore_0] += 1; } break; case astore_0: case astore_1: case astore_2: case astore_3: sp -= 1; vars[cop - astore_0].mask = sp->mask; vars[cop - astore_0].min = sp->min; vars[cop - astore_0].max = sp->max; if (vars[cop - astore_0].type == tp_void) { vars[cop - astore_0].type = tp_object; } vars[cop - astore_0].equals = sp->equals; var_store_count[cop - astore_0] += 1; #ifdef DUMP_STACK for (i = 0; i <= cop-astore_0; i++) { printf(" := %x %s\n", i, (int)vars[i].equals, vars[i].equals == NULL ? "" : vars[i].equals->name.as_asciz() ); } #endif break; case fstore_0: case fstore_1: case fstore_2: case fstore_3: sp -= 1; break; case lstore_0: case lstore_1: case lstore_2: case lstore_3: { int index = cop - lstore_0; sp -= 2; vars[index].type = tp_long; vars[index].max = sp[0].max; vars[index].min = sp[0].min; vars[index].mask = sp[0].mask; vars[index+1].max = sp[1].max; vars[index+1].min = sp[1].min; vars[index+1].mask = sp[1].mask; var_store_count[index] += 1; } break; case dstore_0: case dstore_1: case dstore_2: case dstore_3: sp -= 2; break; case iastore: case fastore: case aastore: case bastore: case castore: case sastore: sp -= 3; check_array_index(addr, sp); break; case lastore: case dastore: sp -= 4; check_array_index(addr, sp); break; case pop: sp -= 1; break; case pop2: sp -= 2; break; case dup_x0: *sp = *(sp-1); sp += 1; break; case dup_x1: sp[0]=sp[-1]; sp[-1]=sp[-2]; sp[-2]=sp[0]; sp++; break; case dup_x2: sp[0]=sp[-1]; sp[-1]=sp[-2]; sp[-2]=sp[-3]; sp[-3]=sp[0]; sp++; break; case dup2_x0: sp[0]=sp[-2]; sp[1] = sp[-1]; sp += 2; break; case dup2_x1: sp[1]=sp[-1]; sp[0]=sp[-2]; sp[-1]=sp[-3]; sp[-2]=sp[1]; sp[-3]=sp[0]; sp += 2; break; case dup2_x2: sp[1]=sp[-1]; sp[0]=sp[-2]; sp[-1]=sp[-3]; sp[-2] = sp[-4]; sp[-3]=sp[1]; sp[-4] = sp[0]; sp += 2; break; case Jswap: { vbm_operand tmp = sp[-1]; sp[-1] = sp[-2]; sp[-2] = tmp; } break; case iadd: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "+"); } if (((sp[-1].max ^ sp[-2].max) < 0 || ((sp[-1].max + sp[-2].max) ^ sp[-1].max) >= 0) && ((sp[-1].min ^ sp[-2].min) < 0 || ((sp[-1].min + sp[-2].min) ^ sp[-1].min) >= 0)) { sp[-2].max += sp[-1].max; sp[-2].min += sp[-1].min; } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } sp[-2].mask = make_mask(minimum(first_set_bit(sp[-2].mask), first_set_bit(sp[-1].mask))); sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case isub: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "-"); } if (((sp[-2].max ^ sp[-1].min) >= 0 || ((sp[-2].max - sp[-1].min) ^ sp[-2].max) >= 0) && ((sp[-2].min ^ sp[-1].max) >= 0 || ((sp[-2].min - sp[-1].max) ^ sp[-2].min) >= 0)) { sp[-2].max -= sp[-1].min; sp[-2].min -= sp[-1].max; } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } sp[-2].mask = make_mask(minimum(first_set_bit(sp[-2].mask), first_set_bit(sp[-1].mask))); sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case imul: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "*"); } sp[-2].mask = make_mask(first_set_bit(sp[-2].mask) + first_set_bit(sp[-1].mask)); if (sp[-2].mask == 0) { message(msg_zero_result, addr, "*"); sp[-2].max = sp[-2].min = 0; } else { vbm_operand result; if (calculate_multiply_range(result, sp[-2], sp[-1])) { assert(result.min <= result.max); sp[-2].max = result.max; sp[-2].min = result.min; } else { // overflow sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } } sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case idiv: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "/"); } if ((sp[-1].min > 0 && (sp[-2].max < sp[-1].min && sp[-2].min > -sp[-1].min)) || (sp[-1].max < 0 && (-sp[-2].max > sp[-1].max && sp[-2].min > sp[-1].max))) { message(msg_zero_result, addr, "/"); sp[-2].min = sp[-2].max = 0; } else if (sp[-1].min > 0) { if (sp[-2].max < 0) { sp[-2].min = sp[-2].min/sp[-1].min; sp[-2].max = sp[-2].max/sp[-1].max; } else if (sp[-2].min < 0) { sp[-2].min = sp[-2].min/sp[-1].min; sp[-2].max = sp[-2].max/sp[-1].min; } else { sp[-2].min = sp[-2].min/sp[-1].max; sp[-2].max = sp[-2].max/sp[-1].min; } } else { sp[-2].min = ranges[tp_int].min; sp[-2].max = ranges[tp_int].max; } sp[-2].mask = make_mask(first_set_bit(sp[-2].mask) - first_set_bit(sp[-1].mask)); sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case irem: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "%"); } if ((sp[-1].min > 0 && (sp[-2].max < sp[-1].min && sp[-2].min > -sp[-1].min)) || (sp[-1].max < 0 && (-sp[-2].max > sp[-1].max && sp[-2].min > sp[-1].max))) { message(msg_no_effect, addr); } else if (sp[-1].min > 0) { sp[-2].min = (sp[-2].min < 0) ? 1-sp[-1].max : 0; sp[-2].max = sp[-1].max-1; } else { sp[-2].min = ranges[tp_int].min; sp[-2].max = ranges[tp_int].max; } sp[-2].mask = ALL_BITS; sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case ishl: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "<<"); } if (sp[-1].max < 0) { message(msg_shift_count, addr, "<<", "<=", (void*)sp[-1].max); } else if(sp[-1].min >= 32) { message(msg_shift_count, addr, "<<", ">=", (void*)sp[-1].min); } else if ((unsigned(sp[-1].max - sp[-1].min) < 256) && (sp[-1].max >= 32 || sp[-1].min < 0)) { // // Produce this message only if range of expression was // restricted in comparence with domains of builtin types // message(msg_shift_range, addr, "<<", (void*)sp[-1].min, (void*)sp[-1].max); } sp[-2].mask = make_lshift_mask(sp[-2].mask, sp[-1].min, sp[-1].max); if (sp[-2].mask == 0) { message(msg_zero_result, addr, "<<"); } if ((unsigned)sp[-1].max < 32 && (unsigned)sp[-1].min < 32) { if (sp[-2].max >= 0) { if (sp[-2].max << sp[-1].max >> sp[-1].max == sp[-2].max){ sp[-2].max = sp[-2].max << sp[-1].max; } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } } else { if (sp[-2].max << sp[-1].min >> sp[-1].min == sp[-2].max){ sp[-2].max = sp[-2].max << sp[-1].min; } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } } if (sp[-2].min >= 0) { sp[-2].min <<= sp[-1].min; } else { if (sp[-2].min << sp[-1].max >> sp[-1].max == sp[-2].min){ sp[-2].min = sp[-2].min << sp[-1].max; } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } } } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case ishr: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, ">>"); } if (sp[-1].max < 0) { message(msg_shift_count, addr, ">>", "<=", (void*)sp[-1].max); } else if(sp[-1].min >= 32) { message(msg_shift_count, addr, ">>", ">=", (void*)sp[-1].min); } else if ((unsigned(sp[-1].max - sp[-1].min) < 256) && (sp[-1].max >= 32 || sp[-1].min < 0)) { // // Produce this message only if range of expression was // restricted in comparence with domains of builtin types // message(msg_shift_range, addr, ">>", (void*)sp[-1].min, (void*)sp[-1].max); } sp[-2].mask = make_rshift_mask(sp[-2].mask, sp[-1].min, sp[-1].max); if (sp[-2].mask == 0) { message(msg_zero_result, addr, ">>"); } if ((unsigned)sp[-1].max < 32 && (unsigned)sp[-1].min < 32) { sp[-2].max = (sp[-2].max >= 0) ? sp[-2].max >> sp[-1].min : sp[-2].max >> sp[-1].max; sp[-2].min = (sp[-2].min >= 0) ? sp[-2].min >> sp[-1].max : sp[-2].min >> sp[-1].min; } else { if (sp[-2].max < 0) sp[-2].max = -1; if (sp[-2].min > 0) sp[-2].min = 0; } sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case iushr: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, ">>>"); } if (sp[-1].max < 0) { message(msg_shift_count, addr, ">>>", "<=", (void*)sp[-1].max); } else if(sp[-1].min >= 32) { message(msg_shift_count, addr, ">>>", ">=", (void*)sp[-1].min); } else if ((unsigned(sp[-1].max - sp[-1].min) < 256) && (sp[-1].max >= 32 || sp[-1].min < 0)) { // // Produce this message only if range of expression was // restricted in comparence with domains of builtin types // message(msg_shift_range, addr, ">>>", (void*)sp[-1].min, (void*)sp[-1].max); } sp[-2].mask = make_rushift_mask(sp[-2].mask, sp[-1].min, sp[-1].max); if (sp[-2].mask == 0) { message(msg_zero_result, addr, ">>>"); } if ((unsigned)sp[-1].max < 32 && (unsigned)sp[-1].min < 32 && sp[-2].min >= 0) { sp[-2].max >>= sp[-1].min; sp[-2].min >>= sp[-1].max; } else { sp[-2].max = ranges[tp_int].max; sp[-2].min = ranges[tp_int].min; } sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case iand: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "&"); } if (*pc == ifeq || *pc == ifne) { if (sp[-1].index >= 0 && sp[-2].min == sp[-2].mask && sp[-2].max == sp[-2].mask) { sp[-2].index = sp[-1].index; } else if (sp[-2].index < 0 || sp[-1].min != sp[-1].mask || sp[-1].max != sp[-1].mask) { sp[-2].index = NO_ASSOC_VAR; } } else { sp[-2].index = NO_ASSOC_VAR; } if ((sp[-2].mask &= sp[-1].mask) == 0) { message(msg_zero_result, addr, "&"); } sp[-2].min = sp[-2].mask < 0 ? ranges[tp_int].min : 0; sp[-2].max = (sp[-1].max & sp[-2].max) >= 0 ? sp[-2].mask & ranges[tp_int].max : 0; sp[-2].type = tp_int; sp -= 1; break; case ior: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "|"); } sp[-2].mask |= sp[-1].mask; sp[-2].min = minimum(sp[-1].min, sp[-2].min); sp[-2].max = sp[-2].mask & ranges[tp_int].max; if (sp[-2].min > sp[-2].max) { sp[-2].max = sp[-2].min; } sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case ixor: if ((sp[-1].min == 0 && sp[-1].max == 0) || (sp[-2].min == 0 && sp[-2].max == 0)) { message(msg_zero_operand, addr, "^"); } sp[-2].mask |= sp[-1].mask; sp[-2].min = sp[-2].mask < 0 ? ranges[tp_int].min : 0; sp[-2].max = sp[-2].mask & ranges[tp_int].max; sp[-2].type = tp_int; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; #ifdef INT8_DEFINED case ladd: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "+"); } if (((right_max ^ left_max) < 0 || ((right_max + left_max) ^ right_max) >= 0) && ((right_min ^ left_min) < 0 || ((right_min + left_min) ^ right_min) >= 0)) { left_max += right_max; left_min += right_min; } else { left_min = INT8_MIN; left_max = INT8_MAX; } left_mask = make_int8_mask(minimum(first_set_bit(right_mask), first_set_bit(left_mask))); STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, left_mask); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lsub: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "-"); } if (((left_max ^ right_min) >= 0 || ((left_max - right_min) ^ left_max) >= 0) && ((left_min ^ right_max) >= 0 || ((left_min - right_max) ^ left_min) >= 0)) { left_max -= right_min; left_min -= right_max; } else { left_max = INT8_MAX; left_min = INT8_MIN; } left_mask = make_int8_mask(minimum(first_set_bit(left_mask), first_set_bit(right_mask))); STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, left_mask); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lmul: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "*"); } left_mask = make_int8_mask(first_set_bit(left_mask) + first_set_bit(right_mask)); if (left_mask == 0) { message(msg_zero_result, addr, "*"); STORE_INT8(sp-4, min, 0); STORE_INT8(sp-4, max, 0); } else { if (!calculate_int8_multiply_range(sp-4, sp-2)) { STORE_INT8(sp-4, min, INT8_MIN); STORE_INT8(sp-4, max, INT8_MAX); } } sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lidiv: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "/"); } if ((right_min > 0 && (left_max < right_min && left_min > -right_min)) || (right_max < 0 && (-left_max > right_max && left_min > right_max))) { message(msg_zero_result, addr, "/"); left_min = left_max = 0; } else if (right_min > 0) { if (left_max < 0) { left_min = left_min/right_min; left_max = left_max/right_max; } else if (left_min < 0) { left_min = left_min/right_min; left_max = left_max/right_min; } else { left_min = left_min/right_max; left_max = left_max/right_min; } } else { left_min = INT8_MIN; left_max = INT8_MAX; } left_mask = make_int8_mask(first_set_bit(left_mask) - first_set_bit(right_mask)); STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, left_mask); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lrem: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "%"); } if ((right_min > 0 && (left_max < right_min && left_min > -right_min)) || (right_max < 0 && (-left_max > right_max && left_min > right_max))) { message(msg_no_effect, addr); } else if (right_min > 0) { left_min = (left_min < 0) ? 1-right_max : 0; left_max = right_max-1; } else { left_min = INT8_MIN; left_max = INT8_MAX; } STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, INT8_ALL_BITS); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lshl: sp -= 1; LOAD_INT8_OPERAND(); if ((sp->min == 0 && sp->max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "<<"); } if (sp->max < 0) { message(msg_shift_count, addr, "<<", "<=", (void*)sp->max); } else if(sp->min >= 64) { message(msg_shift_count, addr, "<<", ">=", (void*)sp->min); } else if ((unsigned(sp->max - sp->min) < 256) && (sp->max >= 64 || sp->min < 0)) { // // Produce this message only if range of expression was // restricted in comparence with domains of builtin types // message(msg_shift_range, addr, "<<", (void*)sp->min, (void*)sp->max); } left_mask = make_int8_lshift_mask(left_mask, sp->min, sp->max); if (left_mask == 0) { message(msg_zero_result, addr, "<<"); } if ((unsigned)sp->max < 64 && (unsigned)sp->min < 64) { if (left_max >= 0) { if (left_max << sp->max >> sp->max == left_max){ left_max = left_max << sp->max; } else { left_max = INT8_MAX; left_min = INT8_MIN; } } else { if (left_max << sp->min >> sp->min == left_max){ left_max = left_max << sp->min; } else { left_max = INT8_MAX; left_min = INT8_MIN; } } if (left_min >= 0) { left_min <<= sp->min; } else { if (left_min << sp->max >> sp->max == left_min){ left_min = left_min << sp->max; } else { left_max = INT8_MAX; left_min = INT8_MIN; } } } else { left_max = INT8_MAX; left_min = INT8_MIN; } STORE_INT8(sp-2, min, left_min); STORE_INT8(sp-2, max, left_max); STORE_INT8(sp-2, mask, left_mask); sp[-2].index = NO_ASSOC_VAR; break; case lshr: sp -= 1; LOAD_INT8_OPERAND(); if ((sp->min == 0 && sp->max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, ">>"); } if (sp->max < 0) { message(msg_shift_count, addr, ">>", "<=", (void*)sp->max); } else if(sp->min >= 64) { message(msg_shift_count, addr, ">>", ">=", (void*)sp->min); } else if ((unsigned(sp->max - sp->min) < 256) && (sp->max >= 64 || sp->min < 0)) { // // Produce this message only if range of expression was // restricted in comparence with domains of builtin types // message(msg_shift_range, addr, ">>", (void*)sp->min, (void*)sp->max); } left_mask = make_int8_rshift_mask(left_mask, sp->min, sp->max); if (left_mask == 0) { message(msg_zero_result, addr, ">>"); } if ((unsigned)sp->max < 64 && (unsigned)sp->min < 64) { left_max = (left_max >= 0) ? left_max >> sp->min : left_max >> sp->max; left_min = (left_min >= 0) ? left_min >> sp->max : left_min >> sp->min; } else { if (left_max < 0) left_max = -1; if (left_min > 0) left_min = 0; } STORE_INT8(sp-2, min, left_min); STORE_INT8(sp-2, max, left_max); STORE_INT8(sp-2, mask, left_mask); sp[-2].index = NO_ASSOC_VAR; break; case lushr: sp -= 1; LOAD_INT8_OPERAND(); if ((sp->min == 0 && sp->max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, ">>>"); } if (sp->max < 0) { message(msg_shift_count, addr, ">>>", "<=", (void*)sp->max); } else if(sp->min >= 64) { message(msg_shift_count, addr, ">>>", ">=", (void*)sp->min); } else if ((unsigned(sp->max - sp->min) < 256) && (sp->max >= 64 || sp->min < 0)) { // // Produce this message only if range of expression was // restricted in comparence with domains of builtin types // message(msg_shift_range, addr, ">>>", (void*)sp->min, (void*)sp->max); } left_mask = make_int8_rushift_mask(left_mask, sp->min, sp->max); if (left_mask == 0) { message(msg_zero_result, addr, ">>>"); } if ((unsigned)sp->max < 64 && (unsigned)sp->min < 64 && left_min >= 0) { left_max >>= sp->min; left_min >>= sp->max; } else { left_max = INT8_MAX; left_min = INT8_MIN; } STORE_INT8(sp-2, min, left_min); STORE_INT8(sp-2, max, left_max); STORE_INT8(sp-2, mask, left_mask); sp[-2].index = NO_ASSOC_VAR; break; case land: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "&"); } if ((left_mask &= right_mask) == 0) { message(msg_zero_result, addr, "&"); } left_min = left_mask < 0 ? INT8_MIN : 0; left_max = (right_max & left_max) >= 0 ? left_mask & INT8_MAX : 0; STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, left_mask); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lor: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "|"); } left_mask |= right_mask; if (right_min < left_min) { left_min = right_min; } left_max = left_mask & INT8_MAX; if (left_max < left_min) { left_max = left_min; } STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, left_mask); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lxor: LOAD_INT8_OPERANDS(); if ((right_min == 0 && right_max == 0) || (left_min == 0 && left_max == 0)) { message(msg_zero_operand, addr, "^"); } left_mask |= right_mask; left_min = left_mask < 0 ? INT8_MIN : 0; left_max = left_mask & INT8_MAX; STORE_INT8(sp-4, min, left_min); STORE_INT8(sp-4, max, left_max); STORE_INT8(sp-4, mask, left_mask); sp[-4].index = NO_ASSOC_VAR; sp -= 2; break; case lneg: LOAD_INT8_OPERAND(); if (left_min == left_max) { left_min = left_max = left_mask = -left_max; } else { int8 min = left_min; left_min = (left_max == INT8_MAX)? INT8_MIN : -left_max; left_max = (min == INT8_MIN) ? INT8_MAX : -min; left_mask = make_mask(first_set_bit(left_mask)); } STORE_INT8(sp-2, min, left_min); STORE_INT8(sp-2, max, left_max); STORE_INT8(sp-2, mask, left_mask); sp[-2].index = NO_ASSOC_VAR; break; #else case ladd: case lsub: case lmul: case lidiv: case lrem: case lor: case lxor: case land: sp -= 2; break; case lshl: case lshr: case lushr: sp -= 1; break; case lneg: break; #endif case fadd: case fsub: case fmul: case fdiv: case frem: sp -= 1; break; case dadd: case dsub: case dmul: case ddiv: case drem: sp -= 2; break; case ineg: if (sp[-1].min == sp[-1].max) { sp[-1].mask = sp[-1].min = sp[-1].max = -sp[-1].max; } else { int min = sp[-1].min; sp[-1].min = (sp[-1].max == ranges[tp_int].max) ? ranges[tp_int].min : -sp[-1].max; sp[-1].max = (min == ranges[tp_int].min) ? ranges[tp_int].max : -min; sp[-1].mask = make_mask(first_set_bit(sp[-1].mask)); } sp[-1].type = tp_int; sp[-1].index = NO_ASSOC_VAR; break; case fneg: case dneg: break; case iinc: { var_desc* var = &vars[*pc]; var_store_count[*pc++] += 1; int add = (signed char)*pc++; if (((var->max^add) < 0 || ((var->max+add) ^ add) >= 0) && ((var->min^add) < 0 || ((var->min+add) ^ add) >= 0)) { var->max += add; var->min += add; } else { var->max = ranges[tp_int].max; var->min = ranges[tp_int].min; } var->mask = make_mask(maximum(last_set_bit(var->mask), last_set_bit(add))+1, minimum(first_set_bit(var->mask), first_set_bit(add))); } break; case i2l: if (prev_cop == imul || prev_cop == ishl) { message(msg_overflow, addr); } sp->min = sp[-1].min; sp->max = sp[-1].max; sp->mask = sp[-1].mask; sp->type = tp_long; sp[-1].max >>= 31; // extend sign sp[-1].min >>= 31; sp[-1].mask >>= 31; sp[-1].type = tp_long; sp[-1].index = NO_ASSOC_VAR; sp += 1; break; case f2l: sp += 1; nobreak; case d2l: sp[-2].type = tp_long; sp[-2].index= NO_ASSOC_VAR; sp[-2].min = 0x80000000; sp[-2].max = 0x7fffffff; sp[-2].mask = 0xffffffff; sp[-1].type = tp_long; sp[-1].min = 0x00000000; sp[-1].max = 0xffffffff; sp[-1].mask = 0xffffffff; break; case i2d: case f2d: sp[-1].type = sp[0].type = tp_double; sp[-1].index = sp[0].index = NO_ASSOC_VAR; sp += 1; break; case i2b: if (sp[-1].max < ranges[tp_byte].min || sp[-1].min > ranges[tp_byte].max) { message(msg_conversion, addr, "byte"); } else if ((sp[-1].mask << 24 >> 23) != (sp[-1].mask << 1) && sp[-1].mask != 0xffff // char to byte && sp[-1].mask != 0xff) // int & 0xFF to byte { message(msg_truncation, addr, "byte"); } if (sp[-1].max > ranges[tp_byte].max || sp[-1].min < ranges[tp_byte].min) { sp[-1].max = ranges[tp_byte].max; sp[-1].min = ranges[tp_byte].min; } sp[-1].mask = sp[-1].mask << 24 >> 24; sp[-1].type = tp_byte; sp[-1].index = NO_ASSOC_VAR; break; case i2f: sp[-1].type = tp_float; sp[-1].index = NO_ASSOC_VAR; break; case l2i: #ifdef INT8_DEFINED LOAD_INT8_OPERAND(); if (left_max < ranges[tp_int].min || left_min > ranges[tp_int].max) { message(msg_conversion, addr, "int"); } else if (sp[-2].mask != 0 && sp[-2].mask != ~0) { message(msg_truncation, addr, "int"); } if (left_min >= ranges[tp_int].min && left_max <= ranges[tp_int].max) { sp[-2].min = sp[-1].min; sp[-2].max = sp[-1].max; } else { sp[-2].min = ranges[tp_int].min; sp[-2].max = ranges[tp_int].max; } #else sp[-2].min = ranges[tp_int].min; sp[-2].max = ranges[tp_int].max; #endif sp[-2].type = tp_int; sp[-2].mask = sp[-1].mask; sp[-2].index = NO_ASSOC_VAR; sp -= 1; break; case d2i: sp -= 1; sp[-1].type = tp_int; sp[-1].min = ranges[tp_int].min; sp[-1].max = ranges[tp_int].max; sp[-1].mask = ALL_BITS; sp[-1].index = NO_ASSOC_VAR; break; case l2f: case d2f: sp -= 1; sp[-1].type = tp_float; sp[-1].index = NO_ASSOC_VAR; break; case l2d: sp[-1].type = sp[-2].type = tp_double; sp[-1].index = sp[-2].index = NO_ASSOC_VAR; break; case f2i: sp[-1].type = tp_int; sp[-1].min = ranges[tp_int].min; sp[-1].max = ranges[tp_int].max; sp[-1].mask = ALL_BITS; sp[-1].index = NO_ASSOC_VAR; break; case i2c: if (sp[-1].max < ranges[tp_char].min || sp[-1].min > ranges[tp_char].max) { message(msg_conversion, addr, "char"); } else if ((sp[-1].mask & 0x7fff0000) != 0 && (sp[-1].mask & 0x7fff0000) != 0x7fff0000) { message(msg_truncation, addr, "char"); } if (sp[-1].max > ranges[tp_char].max || sp[-1].min < ranges[tp_char].min) { sp[-1].min = ranges[tp_char].min; sp[-1].max = ranges[tp_char].max; } sp[-1].mask &= 0xFFFF; sp[-1].type = tp_char; sp[-1].index = NO_ASSOC_VAR; break; case i2s: if (sp[-1].max < ranges[tp_short].min || sp[-1].min > ranges[tp_short].max) { message(msg_conversion, addr, "short"); } else if ((sp[-1].mask << 16 >> 15) != (sp[-1].mask << 1)) { message(msg_truncation, addr, "short"); } if (sp[-1].max > ranges[tp_short].max || sp[-1].min < ranges[tp_short].min) { sp[-1].max = ranges[tp_short].max; sp[-1].min = ranges[tp_short].min; } sp[-1].mask = sp[-1].mask << 16 >> 16; sp[-1].type = tp_short; sp[-1].index = NO_ASSOC_VAR; break; case lcmp: #ifdef INT8_DEFINED LOAD_INT8_OPERANDS(); if (*pc == ifeq || *pc == ifne) { if ((left_mask & right_mask) == 0 && !((left_min == 0 && left_max == 0) || (right_min == 0 && right_max == 0))) { message(msg_disjoint_mask, addr); } } if (left_min > right_max || left_max < right_min || ((*pc == ifge || *pc == iflt) && left_min >= right_max) || ((*pc == ifle || *pc == ifgt) && left_max <= right_min)) { message(msg_same_result, addr); } else if (((*pc == ifgt || *pc == ifle) && left_min == right_max) || ((*pc == iflt || *pc == ifge) && left_max == right_min)) { message(msg_weak_cmp, addr); } #endif sp -= 4; sp->type = tp_int; sp->min = -1; sp->max = 1; sp->mask = ALL_BITS; sp->index = NO_ASSOC_VAR; sp += 1; break; case dcmpl: case dcmpg: sp -= 2; nobreak; case fcmpl: case fcmpg: sp -= 2; sp->type = tp_int; sp->min = -1; sp->max = 1; sp->mask = ALL_BITS; sp->index = NO_ASSOC_VAR; sp += 1; break; case ifeq: case ifne: case iflt: case ifge: case ifgt: case ifle: sp -= 1; if (sp->min > 0 || sp->max < 0 || sp->min == sp->max || (sp->min == 0 && (cop == iflt || cop == ifge)) || (sp->max == 0 && (cop == ifgt || cop == ifle))) { message(msg_same_result, addr); } else if ((sp->min == 0 && (cop == ifle || cop == ifgt)) || (sp->max == 0 && (cop == ifge || cop == iflt))) { message(msg_weak_cmp, addr); } pc += 2; break; case if_icmpeq: case if_icmpne: if (sp[-2].min == sp[-2].max && sp[-1].min == sp[-1].max) { message(msg_same_result, addr); } else if ((sp[-2].mask & sp[-1].mask) == 0 && sp[-2].type != tp_bool && sp[-1].type != tp_bool) { message(msg_disjoint_mask, addr); } nobreak; case if_icmplt: case if_icmpge: case if_icmpgt: case if_icmple: if (sp[-2].min > sp[-1].max || sp[-2].max < sp[-1].min || ((cop == if_icmpge || cop == if_icmplt) && sp[-2].min >= sp[-1].max) || ((cop == if_icmple || cop == if_icmpgt) && sp[-2].max <= sp[-1].min)) { message(msg_same_result, addr); } else if (((cop == if_icmpgt || cop == if_icmple) && sp[-2].min == sp[-1].max) || ((cop == if_icmplt || cop == if_icmpge) && sp[-2].max == sp[-1].min)) { message(msg_weak_cmp, addr); } if ((sp[-1].type == tp_short && sp[-2].type == tp_char) || (sp[-2].type == tp_short && sp[-1].type == tp_char)) { message(msg_short_char_cmp, addr); } sp -= 2; pc += 2; break; case if_acmpeq: case if_acmpne: if (sp[-1].type == tp_string && sp[-2].type == tp_string) { message(msg_string_cmp, addr); } sp -= 2; pc += 2; break; case jsr: case goto_near: pc += 2; break; case ret: pc += 1; break; case tableswitch: { pc += 7 - (addr & 3); int low = unpack4(pc); int high = unpack4(pc+4); sp -= 1; if (low < sp->min) { message(msg_incomp_case, addr, low); } if (high > sp->max) { message(msg_incomp_case, addr, high); } pc += (high - low + 1)*4 + 8; } break; case lookupswitch: { pc += 7 - (addr & 3); int n_pairs = unpack4(pc); pc += 4; sp -= 1; while (--n_pairs >= 0) { int val = unpack4(pc); int label = unpack4(pc+4); pc += 8; if (val < sp->min || val > sp->max || (val & ~sp->mask)) { message(msg_incomp_case, addr+label, val); } } } break; case ireturn: case freturn: case areturn: sp -= 1; break; case dreturn: case lreturn: sp -= 2; break; case vreturn: break; case getfield: sp -= 1; nobreak; case getstatic: { const_ref* field_ref = (const_ref*)constant_pool[unpack2(pc)]; const_name_and_type* nt = (const_name_and_type*)constant_pool[field_ref->name_and_type]; const_utf8* field_name= (const_utf8*)constant_pool[nt->name]; const_utf8* desc = (const_utf8*)constant_pool[nt->desc]; const_class*cls_info=(const_class*)constant_pool[field_ref->cls]; const_utf8* cls_name=(const_utf8*)constant_pool[cls_info->name]; class_desc* obj_cls = class_desc::get(*cls_name); field_desc* field = obj_cls->get_field(*field_name); field->name_and_type = nt; if (cop == getfield) { check_variable_for_null(addr, sp); } if (cls->name == *cls_name) { field->attr |= field_desc::f_used; } if (!in_monitor) { accessors = new access_desc(field, cls, get_line_number(addr), accessors); if (cop == getfield) { if (sp->type == tp_self) { accessors->attr |= access_desc::a_self; } if (sp->mask & var_desc::vs_new) { accessors->attr |= access_desc::a_new; } } } int type = get_type(*desc); sp->type = type; if (IS_INT_TYPE(type)) { sp->min = ranges[type].min; sp->max = ranges[type].max; sp->mask = sp->min|sp->max; } else { sp->mask = var_desc::vs_unknown; sp->min = 0; sp->max = MAX_ARRAY_LENGTH; } sp->equals = field; sp->index = NO_ASSOC_VAR; sp += 1; if (type == tp_long || type == tp_double) { sp->type = type; sp->max = 0xffffffff; sp->min = 0; sp->mask = 0xffffffff; sp[-1].max = 0x7fffffff; sp[-1].min = 0x80000000; sp[-1].mask = 0xffffffff; sp += 1; } pc += 2; #ifdef DUMP_STACK printf(" := %x %s\n", (int)field, field == NULL ? "" : field->name.as_asciz() ); #endif } break; case putfield: case putstatic: { const_ref* field_ref = (const_ref*)constant_pool[unpack2(pc)]; const_name_and_type* nt = (const_name_and_type*)constant_pool[field_ref->name_and_type]; const_utf8* desc = (const_utf8*)constant_pool[nt->desc]; const_class*cls_info=(const_class*)constant_pool[field_ref->cls]; const_utf8* cls_name=(const_utf8*)constant_pool[cls_info->name]; const_utf8* field_name= (const_utf8*)constant_pool[nt->name]; class_desc* obj_cls = class_desc::get(*cls_name); field_desc* field = obj_cls->get_field(*field_name); if (cls->name == *cls_name) { field->attr |= field_desc::f_used; if ((name != "") && (name != "") && (! (cls->locks.owns(is_this)))) { // allow assignments to lock only inside monitor or constructor // should also check whether monitor field is owned (if so, error) if ((cls->locks.owns(field)) || (locksAtEntry.owns(field))) { if (strchr(field->name.as_asciz(), '$') == NULL) { // don't print warning for variable names with $ // because javac sometimes does weird stuff for class locks message_at(msg_lock_assign2, cls->source_file, get_line_number(addr), field); } } else { if (strchr(field->name.as_asciz(), '$') == NULL) { // don't print warning for variable names with $ // because javac sometimes does weird stuff for class locks int linenumber = get_line_number(addr); field->write(linenumber); if (field->equals) { const_cast(field->equals)->write(linenumber); // mark field as written to (const_cast req'd here) } } } } } if (!in_monitor) { accessors = new access_desc(field, cls, get_line_number(addr), accessors); } int type = get_type(*desc); sp -= (type == tp_long || type == tp_double) ? 2 : 1; if (sp->equals != NULL) { if (sp->equals->name_and_type != nt) { /*field_desc* aux = new field_desc(*(sp->equals)); aux->name_and_type = nt; sp->equals = aux; equal_descs.push_back(aux);*/ sp->equals = is_const; } } field->equals = sp->equals; if (cop == putfield) { sp -= 1; if (!in_monitor) { if (sp->type == tp_self) { accessors->attr |= access_desc::a_self; } if (sp->mask & var_desc::vs_new) { accessors->attr |= access_desc::a_new; } } check_variable_for_null(addr, sp); } #ifdef DUMP_STACK printf("%x %s := %x %s\n", (int)field, field_name->as_asciz(), (int)field->equals, field->equals == NULL ? "" : field->equals->name.as_asciz() ); #endif pc += 2; } break; case invokespecial: case invokevirtual: case invokestatic: case invokeinterface: { const_ref* method_ref = (const_ref*)constant_pool[unpack2(pc)]; const_class* cls_info = (const_class*)constant_pool[method_ref->cls]; utf_string cls_name(*(const_utf8*)constant_pool[cls_info->name]); class_desc* obj_cls = class_desc::get(cls_name); const_name_and_type* nt = (const_name_and_type*)constant_pool[method_ref->name_and_type]; utf_string mth_name(*(const_utf8*)constant_pool[nt->name]); utf_string mth_desc(*(const_utf8*)constant_pool[nt->desc]); int n_params = get_number_of_parameters(mth_desc); int fp = n_params; if (cop != invokestatic) { fp += 1; check_variable_for_null(addr, sp-fp); } #ifdef DUMP_STACK printf(" = %s\n", sp[-fp].equals == NULL ? "" : sp[-fp].equals->name.as_asciz() ); #endif if (cls_name == "java/lang/Object") { bool hold_lock = false; Lock wait_on; if (sp[-fp].equals != NULL) { wait_on = sp[-fp].equals; } else { wait_on = NULL; } if (mth_name == "notify" || mth_name == "notifyAll" || mth_name == "wait") { // check whether lock on object which is waited on is owned if (wait_on != NULL) { if (cls->locks.owns(wait_on)) { hold_lock = true; } } if (!hold_lock) { message(msg_wait_nosync, addr, (wait_on != NULL ? wait_on->name.as_asciz() : ""), &mth_name); } } if (mth_name == "wait") { wait_line = get_line_number(addr); attr |= m_wait; // check whether other locks are held int nLocks = cls->locks.nLocks() - (hold_lock? 1:0) > 0; if (nLocks > 0) { message(msg_wait, addr); // print all other locks char buf[MAX_MSG_LENGTH - 40]; // buffer for locks char* out = buf; int n; monitor_stack::const_iterator entry = cls->locks.begin(); while (entry != cls->locks.end()) { if ((n = snprintf(out, sizeof(buf)-(out-buf), " %s,", (*entry)->name.as_asciz())) == -1) { // no space in buffer left - print "..." at end of buffer sprintf(buf+sizeof(buf)-4, "..."); out = buf + sizeof(buf) - 1; break; } out += n; entry++; } *(out-1) = '\0'; message(msg_locklist, addr, cls->locks.nLocks(), buf); n_messages--; // avoid counting message twice } } } // end of wait/notify treatment if ((cop == invokespecial) && (mth_name == "finalize")) { super_finalize = true; } else { method_desc* method = obj_cls->get_method(mth_name, mth_desc); int call_attr = 0; if (cop != invokestatic && sp[-fp].type == tp_self) { call_attr |= callee_desc::i_self; } if (in_monitor) { call_attr |= callee_desc::i_synchronized; } callees = new callee_desc(cls, method, callees, get_line_number(addr), call_attr); if ((cls->locks.nLocks() > 0) && (method->name != "") && (method->name != "") ) { // if locks are currently held... // ... add call from innermost "pseudo method" to method // exception: constructors can't own locks method->attr |= m_concurrent; Lock curr = cls->locks.getInnermost(); if (curr == is_this) { call_attr |= callee_desc::i_self; } if ((curr->cls == cls) && (method->cls == cls)) { // ignore calls to other classes const char* curr_name = compound_name(cls->name.as_asciz(), curr->name.as_asciz()); class_desc* curr_cls = class_desc::get(utf_string(curr_name)); method_desc* caller_method = curr_cls->get_method(utf_string(""), utf_string("()")); // caller_method->attr |= method_desc::m_synchronized; if (caller_method->vertex == NULL) { caller_method->vertex = new graph_vertex(curr_cls); } if ((!(method->locksAtEntry.owns(curr))) && (sp[-fp].equals == is_this) && // (method->cls == cls) (! (attr & m_synchronized)) // synch. methods are already covered by other mechanisms ) { // context at method entry method->locksAtEntry.acquire(curr); // add call graph edge lock. -> method // only if method is of current class // and not added before // callee method->attr |= method_desc::m_sync_block; if (method->vertex == NULL) { method->vertex = new graph_vertex(obj_cls); } method->callees = new callee_desc(obj_cls, method, method->callees, get_line_number(addr), call_attr); // lock was not in list, add to call graph int caller_attr = 0; // caller_attr |= method_desc::m_synchronized; // attr |= m_synchronized; // crucial flag for later analysis? method->build_call_graph(caller_method, method->callees, caller_attr); // add edge for pseudo method to call graph } } } // end of pseudo method -> method call if (n_params < 32) { int mask = 0; for (i = 0; i < n_params; i++) { if ((sp[i-n_params].type == tp_object || sp[i-n_params].type == tp_string) && sp[i-n_params].mask == 0) { mask |= 1 << i; } } if (cop != invokestatic) { // Reference to target object i passed as 0 parameter mask <<= 1; } method->null_parameter_mask |= mask; } } int type = get_type(mth_desc); sp -= fp; if (type != tp_void) { if (IS_INT_TYPE(type)) { sp->min = ranges[type].min; sp->max = ranges[type].max; sp->mask = sp->min|sp->max; } else { sp->min = 0; sp->max = MAX_ARRAY_LENGTH; sp->mask = var_desc::vs_unknown; sp->equals = NULL; // we don't know what we get back! } sp->index = NO_ASSOC_VAR; sp->type = type; sp += 1; if (type == tp_long || type == tp_double) { sp->type = type; sp->max = 0xffffffff; sp->min = 0; sp->mask = 0xffffffff; sp[-1].max = 0x7fffffff; sp[-1].min = 0x80000000; sp[-1].mask = 0xffffffff; sp += 1; } } pc += (cop == invokeinterface) ? 4 : 2; } break; case anew: sp->type = tp_object; sp->mask = var_desc::vs_new|var_desc::vs_not_null; sp->min = 0; sp->max = MAX_ARRAY_LENGTH; sp->index = NO_ASSOC_VAR; sp->equals = getNew(); sp += 1; pc += 2; break; case newarray: sp[-1].type = array_type[*pc++] + indirect; sp[-1].mask = var_desc::vs_new|var_desc::vs_not_null; sp[-1].index = NO_ASSOC_VAR; if (sp[-1].min < 0) { if (sp[-1].max < 0) { message(msg_neg_len, addr, sp[-1].min, sp[-1].max); } else if (sp[-1].min > -128) { message(msg_maybe_neg_len, addr, sp[-1].min, sp[-1].max); } } if (sp[-1].min < 0) { sp[-1].min = 0; if (sp[-1].max < 0) { sp[-1].max = 0; } } break; case anewarray: sp[-1].type = tp_object + indirect; sp[-1].mask = var_desc::vs_new|var_desc::vs_not_null; sp[-1].index = NO_ASSOC_VAR; if (sp[-1].min < 0) { if (sp[-1].max < 0) { message(msg_neg_len, addr, sp[-1].min, sp[-1].max); } else if (sp[-1].min > -128) { message(msg_maybe_neg_len, addr, sp[-1].min, sp[-1].max); } } if (sp[-1].min < 0) { sp[-1].min = 0; if (sp[-1].max < 0) { sp[-1].max = 0; } } pc += 2; break; case arraylength: sp[-1].type = tp_int; sp[-1].mask = make_mask(last_set_bit(sp[-1].max), 0); break; case athrow: sp -= 1; break; case checkcast: pc += 2; break; case instanceof: sp[-1].type = tp_bool; sp[-1].min = 0; sp[-1].max = 1; sp[-1].mask = 1; sp[-1].index = NO_ASSOC_VAR; pc += 2; break; case monitorenter: { in_monitor += 1; sp -= 1; if (sp->equals != NULL) { // mark monitor ownership; use monitor count in later version Lock curr = cls->locks.getInnermost(); cls->locks.acquire(sp->equals); cls->usedLocks.acquire(sp->equals); if (sp->equals == is_this) { attr |= m_synchronized; // mark method as "synchronized" } if (sp->equals->name_and_type != NULL) { class_desc* curr_cls; const class_desc* curr_type; method_desc* caller_method; const char* curr_class_name; if (curr != NULL) { // already holding a lock // get class descriptor of current innermost lock (a) // instead of normal class descriptor, use TYPE.variable_name // this will allow us to distinguish between instances (imperfectly) /* utf_string* curr_class_type = dynamic_cast(constant_pool[curr->name_and_type->desc]); */ // if method is synchronized, use method as caller - otherwise, // use pseudo method OWNER.var_name. if (curr == is_this) { curr_type = curr_cls = cls; caller_method = this; } else { if (curr->name_and_type->name == 0) { curr_cls = cls; } else { curr_cls = curr->cls; } curr_type = curr_cls; // use pseudo class name "owner.name" to distinguish between // fields of different objects curr_class_name = curr_cls->name.as_asciz(); const char* curr_name = compound_name(curr_class_name, curr->name.as_asciz()); curr_cls = class_desc::get(utf_string(curr_name)); caller_method = curr_cls->get_method(utf_string(""), utf_string("()")); // caller_method->attr |= method_desc::m_synchronized; // disable? } } else { // no lock acquired yet in this method // add call graph edge (this method) -> (lock) // get method descriptor of this method caller_method = this; curr_type = curr_cls = cls; curr_class_name = curr_cls->name.as_asciz(); } if (sp->equals->cls == curr_type) { // only analyze locks on fields of current class if (caller_method->vertex == NULL) { caller_method->vertex = new graph_vertex(curr_cls); } // get class descriptor of object type of new lock (b) const char* lock_name = compound_name(sp->equals->cls->name.as_asciz(), // class name sp->equals->name.as_asciz()); // field name class_desc* obj_cls = class_desc::get(utf_string(lock_name)); // get descriptor of dummy method "owner.b." method_desc* method = obj_cls->get_method(utf_string(""), utf_string("()")); method->attr |= method_desc::m_synchronized; if (method->vertex == NULL) { method->vertex = new graph_vertex(obj_cls); } obj_cls->source_file = cls->source_file; // add call from a. to b. int call_attr = 0; call_attr |= callee_desc::i_synchronized; if (sp->equals->cls == cls) { call_attr |= callee_desc::i_self; } method->callees = new callee_desc(obj_cls, method, method->callees, get_line_number(addr), call_attr); if (caller_method->attr & m_sync_block) { method->attr |= m_sync_block; } int caller_attr= 0; caller_attr |= method_desc::m_synchronized; method->build_call_graph(caller_method, method->callees, caller_attr); } #ifdef DUMP_MONITOR printf("%s acquires lock on %s.%s (type %s)\n", cls->name.as_asciz(), sp->equals->cls->name.as_asciz(), sp->equals->name.as_asciz(), ((const_utf8*)constant_pool[sp->equals->name_and_type->desc])->as_asciz() ); #endif } } } break; case monitorexit: { if (in_monitor > 0) { in_monitor -= 1; } sp -= 1; if (sp->equals != NULL) { #ifdef DUMP_MONITOR printf("%s relinquishes lock on %s.%s\n", cls->name.as_asciz(), sp->equals->cls->name.as_asciz(), sp->equals->name.as_asciz() ); #endif cls->locks.release(sp->equals); } } break; case wide: switch (*pc++) { case aload: { var_desc* var = &vars[unpack2(pc)]; if (var->type == tp_void) { var->type = tp_object; var->min = 0; var->max = MAX_ARRAY_LENGTH; var->mask = var_desc::vs_unknown; } sp->type = var->type; sp->min = var->min; sp->max = var->max; sp->mask = var->mask; sp->index = var - vars; sp->equals = var->equals; sp += 1; } break; case iload: { var_desc* var = &vars[unpack2(pc)]; if (var->type == tp_void) { var->type = tp_int; var->min = ranges[tp_int].min; var->max = ranges[tp_int].max; var->mask = ALL_BITS; } sp->type = var->type; sp->min = var->min; sp->max = var->max; sp->mask = var->mask; sp->index = var - vars; sp += 1; } break; case dload: sp[0].type = sp[1].type = tp_double; sp[0].index = sp[1].index = NO_ASSOC_VAR; sp += 2; break; case lload: { int index = unpack2(pc); if (vars[index].type == tp_void) { vars[index].type = tp_long; vars[index].min = 0x80000000; vars[index].max = 0x7fffffff; vars[index].mask = 0xffffffff; vars[index+1].min = 0x00000000; vars[index+1].max = 0xffffffff; vars[index+1].mask = 0xffffffff; } sp->type = tp_long; sp->min = vars[index].min; sp->max = vars[index].max; sp->mask = vars[index].mask; sp->index = index; sp += 1; index += 1; sp->type = tp_long; sp->min = vars[index].min; sp->max = vars[index].max; sp->mask = vars[index].mask; sp += 1; } break; case fload: sp->type = tp_float; sp->index = NO_ASSOC_VAR; sp += 1; break; case istore: { int index = unpack2(pc); var_store_count[index] += 1; var_desc* var = &vars[index]; sp -= 1; var->min = sp->min; var->max = sp->max; var->mask = sp->mask; if (var->type == tp_void) { var->type = tp_int; } } break; case astore: { int index = unpack2(pc); var_store_count[index] += 1; sp -= 1; vars[index].min = sp->min; vars[index].max = sp->max; vars[index].mask = sp->mask; if (vars[index].type == tp_void) { vars[index].type = tp_object; } } break; case fstore: sp -= 1; break; case lstore: { int index = unpack2(pc); sp -= 2; vars[index].type = tp_long; vars[index].max = sp[0].max; vars[index].min = sp[0].min; vars[index].mask = sp[0].mask; vars[index+1].max = sp[1].max; vars[index+1].min = sp[1].min; vars[index+1].mask = sp[1].mask; var_store_count[index] += 1; } break; case dstore: sp -= 2; break; case ret: break; case iinc: { int index = unpack2(pc); var_store_count[index] += 1; var_desc* var = &vars[index]; int add = (short)unpack2(pc+2); if (((var->max^add) < 0 || ((var->max+add) ^ add) >= 0) && ((var->min^add) < 0 || ((var->min+add) ^ add) >= 0)) { var->max += add; var->min += add; } else { var->max = ranges[tp_int].max; var->min = ranges[tp_int].min; } var->mask = make_mask(maximum(last_set_bit(var->mask), last_set_bit(add))+1, minimum(first_set_bit(var->mask), first_set_bit(add))); pc += 2; } } pc += 2; break; case multianewarray: { int dimensions = pc[2]; sp -= dimensions; sp->type = tp_int + indirect*dimensions; sp->index = NO_ASSOC_VAR; sp->mask = var_desc::vs_new|var_desc::vs_not_null; for (int j = 0; j < dimensions; j++) { if (sp[j].min < 0) { if (sp[j].max < 0) { message(msg_neg_len, addr, sp[j].min, sp[j].max); } else if (sp[j].min > -128) { message(msg_maybe_neg_len, addr, sp[j].min, sp[j].max); } } } if (sp->min < 0) { sp->min = 0; if (sp->max < 0) { sp->max = 0; } } sp += 1; pc += 3; break; } case ifnull: case ifnonnull: pc += 2; sp -= 1; break; case jsr_w: case goto_w: pc += 4; break; default: assert(false/*Illegal byte code*/); } prev_cop = cop; } assert(sp == stack_bottom); // pop-up local variables for (ctx = context[code_length]; ctx != NULL; ctx = ctx->next) { sp = ctx->transfer(this, sp, unused, prev_cop); } // reset class fields attributes for (field_desc* field = cls->fields; field != NULL; field = field->next){ field->attr &= ~field_desc::f_used; } if (name == "finalize" && !super_finalize) { message(msg_super_finalize, 0); } // cleanup if (attr & m_synchronized) { // remove "this" from lock set if needed cls->locks.release(is_this); } /* for (field_desc** fd = equal_descs.begin(); fd != equal_descs.end(); fd++) { delete *fd; } equal_descs.clear(); */ for (i = code_length; i >= 0; i--) { local_context* next; for (ctx = context[i]; ctx != NULL; ctx = next) { next = ctx->next; delete ctx; } } delete[] var_store_count; delete[] line_table; delete[] context; } field_desc* method_desc::getNew() { char* name = (char *)malloc((size_t)10); strcpy(name, ""); // assert(equal_descs.size() < 10000); // 4 characters allowed assert(new_cnt < 10000); // 4 characters allowed snprintf(name + strlen(name), 4, "%d", new_cnt++); const char* fd_name = stringPool.add(name); free(name); field_desc* fd = new field_desc(utf_string(fd_name), cls, NULL); // equal_descs.push_back(fd); return fd; } const char* compound_name (const char* first, const char* second) { char* result = (char *)malloc((size_t)(strlen(first) + strlen(second) + 2)); /* Add one byte for '.' and for '\0'. */ strcpy(result, first); char* tmp = result + strlen(first); // point to \0 *tmp = '.'; strcpy(++tmp, second); const char* retval = stringPool.add(result); free(result); return retval; } jlint-3.0/method_desc.hh0000644000175000017500000001044307241077761016046 0ustar davehodaveho00000000000000#ifndef METHOD_DESC_HH #define METHOD_DESC_HH class graph_vertex; class overridden_method; class access_desc; class callee_desc; #include "types.hh" #include "inlines.hh" #include "component_desc.hh" #include "var_desc.hh" #include "constant.hh" #include "class_desc.hh" #include "callee_desc.hh" #include "field_desc.hh" #include "local_context.hh" #include "functions.hh" #include "access_desc.hh" #include "string_pool.hh" #include "locks.hh" class local_context; class method_desc : public component_desc { public: utf_string desc; method_desc* next; int attr; enum { m_static = 0x0008, m_final = 0x0010, m_synchronized = 0x0020, m_native = 0x0100, m_abstract = 0x0400, m_wait = 0x010000, // invoke wait() m_serialized = 0x020000, // method is called only from methods // of related classes m_concurrent = 0x040000, // Method is either run of Runnable protocol // or synchronized or called from them. m_visited = 0x080000, // Used while recursive traversal of methods m_deadlock_free= 0x100000, // Doesn't call any synchronized methods m_override = 0x200000, // Override method of base class m_sync_block = 0x400000 // is called from another method, within a synchronized block }; int n_vars; var_desc* vars; int* var_store_count; bool local_variable_table_present; int in_monitor; callee_desc* callees; access_desc* accessors; graph_vertex* vertex; // // Chain of methods from derived classes, overriding this method // overridden_method* overridden; // // 1 bit in position 'i' of 'null_parameter_mask' means that NULL is // passed as the value of parameter 'i' // unsigned null_parameter_mask; // // 1 bit in position 'i' of 'unchecked_use_mask' means that formal // parameter 'i' is used without check for NULL // unsigned unchecked_use_mask; int code_length; byte* code; local_context** context; int first_line; // line corresponing to first method instruction int wait_line; // line of last wait() invocation in the method word* line_table; Locks locksAtEntry; // locks (potentially) held when method is called // needed to avoid double insertion of "lock -> method" into call graph int demangle_method_name(char* buf); void calculate_attributes(); void find_access_dependencies(); void build_concurrent_closure(); void add_to_concurrent_closure(callee_desc* caller, int call_attr, int depth); void build_call_graph(); bool build_call_graph(method_desc* caller, callee_desc* callee, int call_attr); int print_call_path_to(callee_desc* target, int loop_id, int path_id, int call_attr = 0, callee_desc* prev = NULL); void check_synchronization(); void check_invocations(); bool is_special_method() { return name.first_char() == '<'; } int get_line_number(int pc); void message(int msg_code, int pc, ...); void check_variable_for_null(int pc, vbm_operand* sp); void check_array_index(int pc, vbm_operand* sp); void basic_blocks_analysis(); void parse_code(constant** constant_pool, const field_desc* is_this); method_desc(utf_string const& mth_name, utf_string const& mth_desc, class_desc* cls_desc, method_desc* chain) : component_desc(mth_name, cls_desc), desc(mth_desc) { callees = NULL; accessors = NULL; vertex = NULL; attr = m_serialized; next = chain; first_line = 0; overridden = NULL; local_variable_table_present = false; null_parameter_mask = unchecked_use_mask = 0; new_cnt = 0; // equal_descs.insert(equal_descs.begin(), NULL); } private: int new_cnt; // typedef vector Tequal_descs; // field_descs for "equal" field // Tequal_descs equal_descs; // field_descs for "equal" field field_desc* getNew(); // returns new field desc* and increments counter // returns "first.second", as a string allocated in the string pool }; const char* compound_name(const char* first, const char* second); extern string_pool stringPool; // declared in jlint.cc extern field_desc* is_const; #endif jlint-3.0/mkmf.pl0000755000175000017500000001043507643015103014526 0ustar davehodaveho00000000000000#!/usr/bin/perl ############################################################################### # mkmf.pl # Makefile generator v0.5 written by Cyrille Artho # changes: # 0.1: initial version. # 0.2: better output (each file on one line with \ at the end) and update mode # 0.3: removed stupid bug that produces broken Makefiles # 0.4: removed another stupid bug that omitted the .c files in dependencies... # 0.5: removed unused @hfiles list ############################################################################### # generates a simple default Makefile # a) takes Makefile.top and (optionally) Makefile.bottom and generates # dependencies for .o and .h files in between # b) take existing Makefile (generated by mkmf) and updates automatically # generated part. ############################################################################### # Usage: mkmf.pl target [regexps to ignore] # assumptions: Target = built from all .o files in current directory. # .o files are built with gcc -c. .c* files are scanned for .h[h] files. # dependency for each x.o file: x.c[cp]* x.hh? [any other header files] # ignore regexps are used for filenames, which are not included if they match. ############################################################################### # TODO: # a) better comment line (include date, cmd line args used for generating # Makefile etc.) # b) this tool is not supposed to support bigger projects, so if you need # special features, use autoconf :-) ############################################################################### use File::Find; my $target = shift; my @cfiles; # C files #my @hfiles; # header files my $ignore; # regexps to ignore my $is_cpp; # C or C++ my @input; my $found = 0; die "Usage: mkmf [ignore]\n" unless $target; while (my $tmp = shift) { $ignore .= "$tmp|"; } $ignore =~ s/\|$//; if ($ignore eq "") { $ignore = "\$^"; } # pattern that never matches if (@input = <>) { while (my $line = shift @input) { if ($line !~ /^\# --> automatically generated dependencies/) { print $line; } else { $found = 1; last; } } if (! $found) { die "This input file has not been preprocessed by mkmf before!\n"; } while (my $line = shift @input) { if ($line =~ /^\# --> end of automatically generated dependencies/) { $found = 1; last; } } if (! $found) { die "This input file has not been preprocessed by mkmf before!\n"; } } else { open (FILE, "Makefile.top") or die "Cannot open Makefile.top: $!\n"; print ; close FILE; } print "# --> automatically generated dependencies follow; do not remove this line.\n"; getFiles(); # target print "$target:"; foreach (@cfiles) { /\.c[cpx+]*$/; print " \\\n\t$`.o"; } # print name + .o print "\n\t"; ($is_cpp) = grep /$target\.c[cpx+]*$/, @cfiles; $is_cpp =~ s/.*\.c//; # remove all but suffix (also removes first letter of it) if (is_cpp) { print '$(CPP)'; } else { print '$(CC)'; } print ' $(LFLAGS) -o ', "$target"; foreach (@cfiles) { /\.c[cpx+]*$/; print " $`.o"; } # print name + .o print "\n\n"; # target line finished printDeps(); print "# --> end of automatically generated dependencies; do not remove this line.\n"; open (FILE, "Makefile.bottom") or exit; # no error here print ; close FILE; ### end of main ### sub getFiles { # look for files with right suffixes find (\&checkFile, '.'); } sub checkFile { return if (!(-f)); return unless (/\.[ch][chpx+]*$/); return if /$ignore/io; if (/\.c[cpx+]*$/) { push @cfiles, $_; } else { # push @hfiles, $_; } } sub printDeps { # check dependencies of C/C++ and header files foreach my $file (@cfiles) { $file =~/\.c[cpx+]*$/; print "$`.o: $file"; # print name + .o checkHeaders($file, {}); # print dependencies print "\n\t"; if (is_cpp) { print '$(CPP)'; } else { print '$(CC)'; } print " \$(CFLAGS) $file\n\n"; } } sub checkHeaders { # parse file for #include "..." my $filename = shift; my $deps = shift; my @newfiles; open (FILE, $filename) or die "Could not open $filename: $!\n"; while () { if (s/^\#include\s*\"(.*)\"$/$1/) { if (! defined $$deps{$1}) { print " \\\n\t$1"; $$deps{$1} = 1; push @newfiles, $1; } } } close FILE; foreach $newfile (@newfiles) { checkHeaders($newfile, $deps); } } jlint-3.0/overridden_method.hh0000644000175000017500000000045707236032035017262 0ustar davehodaveho00000000000000#ifndef OVERRIDDEN_METHOD_HH #define OVERRIDDEN_METHOD_HH class method_desc; class overridden_method { public: overridden_method* next; method_desc* method; overridden_method(method_desc* mth, overridden_method* chain) { method = mth; next = chain; } }; #endif jlint-3.0/string_pool.hh0000644000175000017500000000306007645261161016121 0ustar davehodaveho00000000000000#ifndef STRING_POOL_HH #define STRING_POOL_HH #ifdef __GNUC__ #if __GNUC__ > 2 using namespace std; #endif #endif /******************************************************************************/ /* Class that allocates a const char* if not yet present in string pool */ /******************************************************************************/ // table of all locks a thread is holding #ifdef HASH_TABLE #include #else #include #endif #ifdef HASH_TABLE struct eqstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) == 0; } }; typedef hash_set, eqstr> string_table; #else struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; typedef set string_table; #endif class string_pool { public: string_table pool; const char* add(const char* el) { char* entry; string_table::iterator it; if ((it = pool.find(el)) == pool.end()) { // allocate element! entry = strdup(el); pool.insert(entry); return const_cast(entry); } else { return *it; } } void remove(const char* el) { string_table::iterator entry; if ((entry = pool.find(el)) != pool.end()) { // free space for element free(const_cast(*entry)); pool.erase(entry); } } void clear() { for (string_table::iterator it = pool.begin(); it != pool.end(); it++) { free(const_cast(*it)); pool.erase(it); } } }; #endif jlint-3.0/tests.tar.bz20000644000175000017500000015274010065643720015614 0ustar davehodaveho00000000000000BZh91AY&SY3? ft5l{s췻$P}{y{{p{{=}muV u wh]+TR[}r}t[}6{.0@sM-W^xyt́LN*J((J(U(U)A@ U()REAEPA@  " W>=; kb{{y]Wz=;yu5R} 'YSiw.vR*!+>չ{{r=x`2:Mea@4F˸+Oe=((=h4 RH(*B   tMҀ@tPh( Pؠ dU@@4IJXVYg_N-}tn۽Θ%LCLbh4ѩ@! ddiS 'I&&j4@ @CDѤOMhFމOOIMԛ<FOM6iM(l6z 44S$jOHiCA42 @jH&lQ3Sh4MSSj=@=A=@!!A h@DD1LM43ĉc)=OOCI66GH=Lji#dF @ hM@Ѡ4&0FИ4= 2c(='zzOI54ѱ`+Jp1 6Cj?;U#_ܗo}&BH7e"T6:R8O H*$WK\W;5{Mb)#S_S٭Xt/Mp Uү) \*ƲFVݖRLW!v}ȗ0+ e#W>X8([@40ĶN|?r1 "bSůb=dI$pj"P|Gm<,G/ - 6!ق!E%(?X?&H@I4XY|e9\G4ySHjCN)P)ʊ(7\;4(#`̸ubS!$*!t1P90!ߋKmÓmh?Aӄ< I8M*^ɬ/y ')PCh RU>e&xODwt|=8m$?D %W)RmR 2bΜFƒp&qBt4 AtsAy%kRO/_w|:˴R%.G4""yJ:0kpT&uji-1YI@FEI%qbly谅yJd .Ja&-)riZq(B\YH8q{$kJQ~~b}}4||vgu| On-–iD m~cԜDʢYWFm\3ncG˺b%]+D|=dr_FMp31<ۍ%H"b+ <$eR-]"4jZiІ} wbAM)skvB-DJPRȌ"F:=X#(f,m "c n^p.ei$|N77ɿ"@AyzHY6Ql\ecC.P[e'Aztƣ1%@ GE7Z!E-Hi1dN8@tٔlQFSfWm@D觍 6 f,+GgWTű}}IEL!NÇ1#3CȈ2TkԵ6l|df6M֦jҏ}x]=@9 m -dR ;A`T79(q9>*2BjR!KEND`qBgژflzXGqp{ذQ_G { (dPul),#(2Q#Ҟƍ3T-da߉gFn!-Znqs:tӔN"mXg6>5A9`U&1.il&yp+lAaj\%~6`Φ'un sD-Oȯ;.k M㷱Mu&Q=P73 X!oپe;$6dEUU2lKV* ׋v!系!Eor>CJ8)(sz}_7\auqWvK`܉SG{Yq?3LZ"qSO!8~_~đM)r`[E)Tp+L3NӣSKf)cNc"taϋsޗfYҰq.C%d2:ӵhP d*:z>N/3~7}7c'}T;r/M'<}W݉nysFOm;)a 1ʔB[99Ǚڀ!eəF1]?U #Û;&?Lӓ;|[%BZ(?5w&&sae"ASb E S] f3<1w`(`yVݤuѿƾelv zkۏh6]SGG)LΎW|>]lq8w/d04f2OFӓ֩G-C{cf[b9:~m~'>~}38&~uygابd)F5Kw3iQut Mܩ˾&9]b/*d Fbou\ .TP =ES&478,.҆s~C莶aw\ev# z̟w?jd}OWonS_ig(k] cTN'F]-P <~n}%rFooJYR*Y6iV w]\O>>AE`i;BqE /7f|q(wy o$jUz2wOTȤ7S=G.`IMA, 6@m ПUpSU[[&f&d* 纫 ;!7SUN kp >s#+( ۶ɇf`I-[LhXAKJ|\և[l ]r]F+u>3KD1Vv:F< ˶"Pz8qOv؂:&xw9Nq,,7X2z7/=>M4G)L1i , bH`t둯xev"é͛3wny׵O7fV+D2%q 7ˉxw q)1Q關2n.h^m:ҊXKηSf+ U3q*LH$pD0ıhCa\75MA׈V'WAw_K72@i՛A9)PpBv DW\rصQ^xآ,^yA<5Eל,riEO[-֝wpwiY2ٙB氳rCWWÉz8FDinJP@60q qO_bLch4( ӣ,\ Q1CR-`J)ݾUEX46xPȱ|.&J?;A8~Nz(A {dWRGDW].[Dc p)/Nt GUQ˔HoaUOQNk}؛3 ÐnƄEB;p'FU좏@is4Ӯi*ԢbPxv4 AQe] tMFcW!(.*㏑P?nKf_뭴ҪׂX#/Ky1JoC:,YoLh]3/KRf"18FL,=3faun;ߓg/ň%Q-EQTTЏK] vzQ"c}(F}fɇ;TN'۸{Z9#ؖdHt$h3 ס=t;l9d`btɋiD`X6Vъ"C]>[)\3m a$_/zQEcKxD}5*l}er\P&,Mm8{9TUD2Ց9ۯH4+,"pf́¦aZ.հ=_\ VG?|BzN*)h[0QLk؉m㑢,X䱂bo^?'!r 4pֵE\um?|N' 沛2{BY3SY(,+XфV%uZ1>޶!A#orUS79-D en E:<)3e?IQk4^QAqpUF/W?2q>nؽQr}.6$%Hg=uO/C71bXmǚ I}'zJFp٣\ `h:ٗ5l57; ֕9̿u4̮&Q ApRJBHADQK억<}./17A-/G Ŭ6$̻iE#vȑ)ؒM&jXLGLc #9)F1%jp3^qz,nLi楝3Jk9yt񛨇3G6>l\OvQWK0mYRT[RyzF;QiQfJ%UIp^,<&{ˀsH"DQES8)mq)cHEQHa̱>M7Xdi}IN}Pj}dJ6Ls>3 k$bdU#eҡ1D6mRD~/캟/V-&$:vvJONF8|oqQk mFhnS]:vRLb㧹*jDɈѹ,vJ1ukQq?+nVR# V*Wz LM\`vmCdr@qpyK) ڞpsRd6A9>"rϭ]PT^,{#[h+ `fWKl[k$V}l&ODUT cs#)W!$$հfY+%h>6 >n"$d8|S+'"3K&* 1V[#!2gz8۩RS%NQ$4_q8OP^t V%7 bwNJEm9*m7mUj(Ĺ%8W1A3C~!]o-)?YZ {N^Sh}/~^4y+FiAo,<$g ,򙿛*Lz#%s͌R\=q{e 16 %u%peex#T0 (^NJp':t96O'~rkIoM:_ii]qyU|{.1M &cl0%OXdhQ. &+ GPZL%!p \K7k$8| :0${>4o\nBg?htKkicgyUm(/|ܥ.kLe2% HRLR=#sq5F\ 럹zE]C1)+URҸ*gn)F,nrH!R|bO{ ^ߤ7S:bhX6۳J@v3'/cuX]5sr*KG( 1P :T~Ӿvo8!-yXE'PEka#6rRLD">PWu]ґ!O8s˷] ĥ0b,par¿%$xsq1I3oc, \\g96?|V.,hEHc5QGv*D?7!~G \6u,v⬻בmn۸ !1 I@,NĄDA oON`DDOGSξrя`a+)Ra]*𷵕`ūjqrBU{^TDB-MǨ>'@%L$<"<GZ9Y,bVMq+1"e8\3+J"izʌ;>x{ZWQ)WFṄ' V/ xkuW#_?$LEILjCI "%wA_~R^>!kB&[m1G)r*eTX}, yRvԛe[ 2 MK:}/w6' c&JR4*Hv ^Դ{v` >[nZ=#.YR?!Cs#V>˥=g4թZ{X/=D%%{ Z^ԈcKqhrюc5Ioh۷2Ie'd/-Y.ssBTP^EF`$R Hw=5%J!,wX`d-adar-!Tz#N (JT{D*gRwZgWihaذ_-[xe,f}*pD0@6I֦w2JCsn`ט>#-?%R!\8jnOY]\R9?A4;Ӱ؁X51KFF$xyytQ%m؁lLT:yK~+TW&F"|"lA_`StLϢ#]mgs~ ,a_XwNZ=fG,\7m^ ƙ"!iIS*$+sFkPO=f7,//??Ԋu+ԃ_bl47 V1{yAccxSgj?l6u9/wóww^0vƧ+s}屌b 8J( @ z۪}N0h'zA /VX\ät'M*O"|,_-"aᅟctA9r҂`s7Yi^Z;IJ"+S?' J, m?'u|/>~-;1ϭMSmf?𔚻0W{"e?I;~Ay WzbV)dc~&y-¯?zwYt"1ʮG袡c {jSvv۽W_,aFr2c(M*w]Wm{'7P^ A@͂D$$+Aa,:Pp 0t ?![.u`0F"Sht-_ݿr#y%'drE_ s@g^1D>ƄJ,(C]f&?z;>)MtzMږ* PX@KM', WSS9#tH~,o*|MP]sؼ+DT>‡x~"foN:yWӬz<]av]HnmTA422PlFScFY_O rKhगFrvkFo Bso6wHG^37RY,Rw.`c\ǘx9~a :sYEC،tRG؉AI RTJJ*O1d*QIbUAV`TDH|u`*ĥR*)T*ª(H "&RU(,Y$dU[-HU*K)bJiLXKK7FCIh|r&a ،UEA@s,Ko$p]پDSZƱZYT X)VFUjRJEiVUUR*$%XKf/w1)6 hiU*RU%(QerԬO1&0M(1? X%E%R4#L`2VR¬gjz4 np09& )NUL /l"4hp">D I=mH3NۆvϽ'$W3MIchòpry; ,t~&B5"M.b3ܝlgj6VvQ` :XUs&[q m.,ki5tl66,$NˑG]$ħ.~8yͱz%0[ϴtGDvBB=ȘLtf<߶{ie/mg=GK;OPuDyLvO!k{sГzޯ?z>җ1i:K_m9ox:{9DpSfϯ8ܝn\NXb?T'&2 F!݋/,YUehRXYIR+C̻ѻ)?CVTC*޹H3c׏VZ%)bWr.eý1ZiѤG HՈb%?I,kEPG VN;po׷^xTimJV1Σɳh~ڇ6 *+)* 124HGɶI#ϟӲzccZel,M,UUX)JJ#jT*}+"UI8V&,a*BjeTFq#_Yi:*UMJJREY'rUT+lN]ԓLYq=_q`ϭSئ"LtS tW*)UTtTbY!?*m4f0R*dsmN$|Mh|ާ+z⩝(cceꏑ%F g~F e)DaSdcK0ñ.hvyuya/ۨ5q9z\Я9F+ =`ڌOq\SSiXz* LiYXcbSD+cgW/B)QUThŜmp4bYEMT}25u+2Y93|9#le%DʉaWkY :(U/x(B(**yس-(rs](?/1X+#9nEL~)bLUcŦ:TaJVbu|T*k=6Za?ˏ}=#ܓΝ F޴OʪRo?ڸO>Wt`D5hX9.^(h?E:LJQo;5rMm#ؔaXs&2 JRQ肥|O{pǩe[х|UhYU4hأGEOK*8jeܣҺ;͒ѝaXEu(Y\HDF7h]E2QffeQQQUUXM Bְ"X[ߵG' $}sDtiEfpkBH EΤh~\BC!|9~7_`vSN&w=\7Xθ >'ϫU~3OH6o?F^>vf_OޚD;ȁA !΀(R;ԠFH\HL%d:ԑصF.AQ&"Td-Zu$S67C9v|B'7a0ڈpd2fԏovÛ0!(VIܡj9߬uH-#ؐ C.+7%ADOyHWTkRGGrUxl8D$Vc,gU nj"*,gN1"4X? *,s0] 0LE# I ּK/]o*NJ 1i8*SQ˹o؞z/qTչqlH=o^'R>7IU[ҔY?pDz.$J-BM@2=։W4L#( SD@p5@$j>ay^I~"d|N?yc-3D㘐*Ȥ*).}DDQפPHH M$b*!UW<[=[61ԙW՝?i# "?ۮ" Y+?AީA |Pꥪ'sF\*K 9Y>eͣʶ%tұD%Em oѻ/A9@ESr%b" z^ BĶĕE3PVlC,"h8@PRRlQRىR}-4JI;2A R,"(UHHړmj*b),U--|$aRJ"UAK!jO?rQJc*H]+VIBdpO$<-±ȕRKGTY*EXNhbȆ,b I)oIڤ`I^6p%Ch%_+>4w܈ʯ=BIG^}#!<+V{p+4Ҥ&,U*K ITOQE8+JTlSN*NTmYlm]WsGʌG)f:ĥEB*وĤӳGNEIIR? {t辗J%)(U5\4э9ѱ0HZKP i͠ *揩^|u}jGzR,i֭tWaEʌQX䟴C.B"[/Fpl.oܲKj@J%a3cJ¼Y*(~OcN꾷bJRªOO:9s̝<$_e=[?BBOTI{$ IH=j&%I}.C}J̥%V1 {>D}bZ=;U.AR>}Ca…O-kbzIv2U_ Z(cEiũ *. 0ղJkIhHL| 1 Ң;LUTUUiQU&E(W\mZI1G]]j~?oY0[C_KWo6єEЅGȤjZU%Rǃ}x!Dn{T5+iˤE`sԗ73 tބ5.wRih WdM4iUUUUWiU[UV4we_ӷm*>S'<jy޶p- {vvEw =cNiE\RhtYJ)|%sJ^5Nէr;jU~G'䰄}(5Gsu; u%]tx#k_ ER+b1Oji R*β_YwrvN\8p *cͦ008lrUm(KVKomO%JJޯC.XNuyHÕJXxcMh.n]DN (%B6+ i1UdbЉw4ӊMɌ,Z{\>M% ^x+d,B1 BLV GKL8K2F u2Qjâ8c)\AUKe|`qMv};OJ"\ÒYEZz̪XQJ20U%C8JD݃WɻRY4G ?wC'LReUz^w#2J:L,NzjB{|$JTN$y5'Si&d_H ͼcCD*TyDՃ'cZS S֦4bUa {lZW94M)m4c֓MipIO3"4Lg[iGQVx5jӵJF(Setʱ"^>iXY6+: %fdʉm܈RsyEuQDUꆙNJTlNxYfXy(X䫽UR&̡lVUW|h\q̓aJ"cw*RzU!A)B2vj\=L;a(lbrt(ɫ o.wR#DQvU/:$5hUzݢHQ׊>nuTPEʦ*ɔvO{\]usjgG5C,`7YQ+.FWpjSw㪒J%ÛuʴeD!GDsE96eVTo%%R[BDZ^L;>sB6U˛EkK4bnf[n'8d\9`۳8l|zm(IEFкdٻNЎ na)D 0XqR][yW*۹ݚұgʱRtc1qa禥RY2NG5*RuYªb*k# *E#E[bW!H<=3f䡫SwGM&Яsx*:V^J%xM4JX(֋) Q.rbXUbhb^Ct49,w$0E9 ݻeh,YQrISfƝΊ30\r8M MTHQW [J6yKGfWK۲l5T !̢ UUR5sQV:V3-ڳ-%҇rdGr5G"5e>O&mйB'ݝ^"VJsJ"C׻}9!\<2%e".˲䫕X`KLէdĢZ;i" ֆU/!;}ʥ/KU}.E'̎ ,4F)ǙzѓExa(]$2NҖ['h۶G6h]=2iɢ|XuQUQŽʪTrJZ=lTvJ5 *jUTR99,e,,QF%YC~heX-AJXcjXĪm1mcaO#ɊB[21ɫHFբ(Y‚DENrp YV/v0b`cƘ`d~9p&{Wyx.hS%VUɔ*!gCKdY_]CF (&WO/%%e:`"=pJHТ'Bli,dmÒ']z6*/9Kɣe]ȫ rTqW+E#4&Iz+28LrQ +5wkBoZQg֯k&] 9&:ROL6OTy+jC!,(Y+5y;;;4sUb,)pV(Ù 0vjv4y;sUSQI;f-/9r;G 4.'] jejy:,hٲJ%У<(ɣ =k).w!w|#Q \9/ Yz#$Rp^LN8m%8eTZ0EƩIRLLTy*uYҔ*+MJѶ44ۃaWN1TJp]S*4ӖuTVѤՋ*XبݒGUeh(TrYW5;`zO$lhh,U $J<U;6 W\7k% ~*|Ws}QF:Qg3FtwUFՈRWW w6~)q([e,2աgfl\J-EzviQwrRQrpPu(T(E># q֪-JdcWy+UVIy."btGvMTau%~]DPd*4AbEUWXΔez%s1NT%:;۫GnrM%!N-%* )eڣt4%(814Qt| ӯvjuR#Ehy%ÆYpW*JOhrU 6,[%ɳYe׸d(4Xhżұ18Wr;׬]4jz񓘍(9PwFl*(QKMI*z KEeT#$TuYIQ(j%©lfY.ʣ-۲Q KK6Y-uYeQSVVQFl*V;ebQ&*m\aUGӕw*p%",%gGsVĴJ5h*e#lrUhb![җ'G6s`PXw]fRFhTrJʩv˾cKUgʵfVQ Ҕ%R+xY䑗2CSFsVV^p>?N䮯d_Ŧ# bmL,i1IV+=jW,(sYdzrSѽ;h(۪<0FHǾ%V6PeVV#w* R²VT%UB/7YT0c.h-L+fX4J9"8wFEec xœgQWex QWخ3I*.]rµ{8QdDY*k׶T;,z'F8Uw.YpǴ'G'[mzs݇R+b1$ :&XtTYbb*JOP*IEd)*|TVۓ؜wk{\e&LbcnC|-s^6>{g8jD*~J3s:P:dES 仧Aى4o( 97#jU l%UقɿxlkeQhÙ/;W󟕷b{4b.H$i%ٻĚ. #SwC+Xx"(tFWU[:cc/~Nc¼^\hs?Y۪,O\WkPRDrQ)QT2@61;؉H7(y@+J}c"ͪ%H޶:4erM4M#eU1#NO_(OO$fd12RaʺexJRGEm%U\TI,B=/QT' A`,:'b\vD/VA\0>(FQ5HppPKП- uBF.*蓃2^ey%=ijjpi%$ǭl]Yy|.Q/E6J^g;*DnJ#grqѣg#KEUpHeA)jD6Acx1y1?MlWK}yND–qoIn} PWUJR*Y+?yY)JĥrMʾU%jVTU.1~kF%עgX봭%Œ(lmdPt,*bX6{),]LR7e*ŬP7IoW}iyzQoHu#v{Kx#K*f~R~䨗[HhAOtJʬa肸:PDDfd,a2Tߖ ki*|zOĬT&>˴<X8tUh#TJ-<'S ۄ&*Mm4i+n+JR<] bUM&cܝ0bJ2[`*.Qar$B\>.d,lpѝY$In\VGe*L쑗 "Ytc͸iՇGyRux dl|<t=d]Fgtz*6E20/=۞!w*BjXW6wEO2W*o7~Q߸~bT<=]ry0Yevz.=QUThҿﳶ߰y8GzZb,=PO>eθ+|W*GClE6+Jю W ta1±Tab8S쓹:4yUr/CGgW:WxT"]**w:; SSб|,GS֋,C$V 37e.D'jdVOgdIСZz^WL7FBHGrRhq^V;|'.1'~w)(b{x U},iccYUpMeBRn*UHE",U˒KU%"a%yFEeTlEsNE77b'SB)] thU7FeRAazg2q4x(&Xve/Kɭ.l((v\4%1B*j~": _/%#fN$;G$]%Y(Q$Z=VC*z۹)% JU]g'bw>czIJʥ+=njz*ά*B*VY tӏ!8X|IDGk&etN^rf8&s d@!Q%V&T~zy?~o+kd(6€ qDfnoJRSz!.XP矆qIRyFU_|/i~>QH.ԷUq*:?/'Cר7]?EU*ZrC$0rJ8fvj"PJ x9}[HU,N-Y0(=/M} ]v[]̒*z0I囮u2Ûveg]Y)+ãֻ0Tvjm:0(fw8Q*ՕVn 0v2[pjaLJ4R8rY)%!(`+%gqw^!Ntti;5;(uK(e5h%X %H+=0cbWBڣŒT+]VΉB{fJRٲuULhUU+кsl LA?fm] T&T4SP4U7Q.UDQQVYg5 ]U䣇 3#v ̛YjJMkU2W-͕ܛ]cv[h1Lҹ)ݢfs)W?R韕~wΧZڪ综uS׬Nm-Rϳ[l K>/Nin"o"^.%wGa(GwTyW^̻g.VB0i 隅  "}dڿxΜڽM>|DaKHgcV UT_1ƷB [lwH/PjM]}rvgV5᝶(E+HbgҾWjKmMӭ*K)]^'wob&lN,۷[s^51/KALG^;ϚBs=⸶joqJҚ51kb! 3]t/rƷ8sonx;; b"#]&i&9uۧbɂvח>^Tm#&L!yxۢjNhV\"c3{r'e# 9if*V}QD|0)qXǏ, l߭)m:QXE_:ƄM.QYQ",i\[=3o 6j*l[ m#Gވc6q֢iㄇ\4URp,ڍsehIMc? .ݒU\?yU\؏Z~{d6*B\Mg/ _hݨ5bt6zgvGi۞o$+Κ)v+zo_Kih _U\! iK4,4BGPJjvf%+3lN}G0oNe/8!5FjUUUma¨jQkW^1ט8kJi-fBŰ"4W1bI#]RΝ'3i6SF3?5,Q13̻UUUD뤙 Dd#M4ղ:;ljѤ;6@}-4x҉ Ii DЅhE~bYccV 7|8]ℴ#pF:&5՚Pb͝g anY-XT("DnyB.9apsvf? y4Wy|ۘ44=8I6^kqFz$@%oRbȴzƳW @B4!^uPWKn7)c_TMtUXС-3ZQѢmNYT6B@$;ц (_YwS*ΚtYstj̾s ճ$/ۿe\KW.6L9e~:}2E >r,ρ'⊓#^Fyʧ&˒DKn }I˨JҴ]cUtltp)]EZ(*YDzRٻj̔mޢ;7;CK;(˒f ړqdD0 $!  /,|!4/xE]y$LFz^M0NoGgziory5,1<4If{ObJ=j'bxww<ꨱĠuPO;ТRu${%/UU+Wۗ VM5Q1$QSV<9l*7JX.{Jq& A$ZP]I>tۣ+jNU{bYOb_JV~RrZaQXH\.r`ZWu>%Lu({;)eݐ:6%,2,}]EUEY$KAKDeXa􏶪ھWoTr2h7~ \$<],$(Kuޤted#/c_dUf5:YJJeD2Ł,UߑM@DyÕ$֓ [hNd=P}OTbM?H7$J؟Dt+Uҧ$G"R{(`USGb* R4TN6H *e,CSi?jڙS? ?/o#Ȕd12TkG6E<#2=2GE*EṴNSd2&bңN #28p;V,M9`'a#)̇{y>K <IkyR.0:]0I:dDT'N ?up+?w6dMG x0 R D%,"~h\+H|wDs&}j#?x#I)$~c_ZJ^WcM%V *i# X]+Os*RO)_cېվd_ˣ/O8y1[欽S'ϰzEGwJJGxZ0ҕZcQEmm4ڴVƕ'؄{_3ܪUUR(XM^,8'WP84vDlF\@iᆘtckDOt~dDqaV*R)lKL!Q,2Ye{TX km=qhXqB*AeC"ђ:1\m_#XtVOW8;"  49A >=U*׌SZӬ|GT= =n[lUG%{Jٌ}mH~TNG .1ы)`$834$ac|IYeQiUJTY,>'zdETYKie q'>+>X=OSŦ0iZ0rTHGB&#N?N3 s59EWp%dOr߁Jۣr巠W {)WVJʱs3%c&[fpNU8JM".TR>~ԢviҚ;%(iKT"PNK!׹*U<V4~y}Z>*)#W\rڟTo#>*lثddNa!$VEiҡHTL@6  (ZtP/#W:!Kʅ[1~g.'!r!LIjN( NpU;Ŗ#)CՑ&;Og%Duz#CD]>yJK?|?~cF$QDH=#T?:6:SlEzM&6S"~'O %Oة6G: ċ:lO8kKuJus!b6s##?#}^ZQ rCp{E{Iqi:1?A0}4{¦V4}\bԪtR,dۖY4lCKnt+mLuD bG5ScH?&jmwU%}ӵ{<_OlUJwc֓lSrEm"Ƥ{z+!O)a `qxEO3v6[=!?ݫIc-=΍>M`ĬQ1&]xrTi:*d,?tF YtE]Eģ pܯ{2B}avSǑ#wďK)})>}u}a\914냳,~tuG}]֟IO0;x4ѡbm<&IO"GއuI T=K1Rj"XK(HB p"yii}"TD}`$C7$O̮>J/iDnXJg#T} ~UUI~f=֟9GRpT-rڬST1,w`,5mIj\/ 8 973{$4!s ueT߱~&?gӫT!U]QRʗΖB$"B*mN\*}xh9m'\*65 8˓ |8J\5}J&A:ay?7 amkDAdf¬j8!znԔjyG1CDpGBcl lI(t `2f*eut}đA"7tvw;0*cjiLJVUƊabcF?W+Q ,fC1&zJ7:x9g5 O6#:Dw+(dQʿE ~hab(ҨG<˛F_TICw>STOUe28jMʈFC$eUUELЎPLA9P g%bl9Տ~eGW᧘v$I3aD4R!G%i(@q(DgX6EIܳpUY,#LXF,$iDR)RXP !!tDpZ ݟIg6ibT^lycBiZ4x8I#QUa VR֧4I?+OiX$ǝU:M?\Q TTN3bhR?\5ܨB"|Id#E3KH3o^yfJ&$9*IGVcDVc b,^!̜=%GRm*mZYrݻzy&KnBMjotE5lRNBZZRzd*dWt2CpR1'Dc:Mb6\F!IZȇO:GE*V**RJE`b"X0"꬟Y',FKNbLdbi]ʼn!7C&$QT%FLX"JcTѩOYQER'*m$E?N7bm+Ht;8wdz1Iaz15+pw}p<[JaQU$UĔ4I$IE>h*Hya zgԝNd褓'#EJCǒOQ<')J $a2 ˲,!**$U.*VCV y0+TyS+Jbi KtF R8 R4v#iO*taBbQ2R1SRCzF $lBQy,DRIl;$z1M)6Ápbta:`Ӧ# a4IiU8|3>f(ۙgdvQˣZ䑢NkkvҶc᥆)jp)8mƒI`ZAbagÏq=>1+bTF*+)*"#0Ȩn/`282xXS &|GVJGŸUS¸F4Qi01c+Mi+fMi)BJ~A*J$/NܟUyaX!}0+c!Lr'E^MG4ҥV:>C㈢%JSU)aa 'SJjU$Ec+dV2L%daRU-cbRb% 5"$hN~1;)=Xlې`*1NJCʏcmE*6ڞmURp٥#E0ja;"a#jGwE=ĈaTTN EJ4GLTAjG_ߙ>rg%-SJ6xW(崩R١**w?T H;*vtTl8s{341A0Mҏ7UFT| X9BbUB#Ol${hSGUO^pQwbc@ҤamUC2IT+;lR E=#%,$G<(MJTdFE[ Uq7aeMJM1uWꈟ^cHr(׽cdpJۂdODDIJ,C-MsSl`*Z*X,L#Ԟ.Ϊ`i܍1:R `Ex5#؞:𒔥UWOt?S#iVWՁ%~)+Q1TR̘Jh]UG*l٩f-WF;w6ں8&*J IϽ$!TMUiNU*piTRJ&(H[%Xi14VC4HWU^m'UT(4'UJ,$RU"6Hv;qI%?rz\cvT~{H*QV.)OF i$fpVvwJ]>,OqFbEhc#J%TJ0-]CΙEs(7RepʯCLV!Ї!zxQ'41EUj 1 NY NSy'^s"77i>tw9(GGEb J1M#C 1&R@zMyCOj>x%?gG9z4c}">L)Y8|b*)ybG##MW|v^C.$bcyL/nۯG'}D**eF# JUUD~*1(Xn¤h։jG5\4H#]u:sׄGC KU.KDnj4t3We*I-y]N璝zz򎳫N2r5*mJ umnQ<\I"AZLJ`ŠC9ЕQ|n]\!*:{Eb*weEPbawySI9ͧ^x1&V [̷h I=jշG$nwI]EhFF:yCrsQmFU*rj413uƱ M*111VǓ jO3thړt]bNta-B! Ȕ^YQA(Iy9q*rmIfbԶZ7bvTuo JJ&IvѪV0jW&1Px' ԾN#T 9Y(E!"Ϊpa .Rr]]GGFb:1 cM*O2GH)CinAQyU^Lbeb]Ƀ,j1'Ip*vXUKk#iɖ"<ct܄V*vTsܸVc3FLQEWTrJ9]u\ed\V\ᓙ*dBXT^&8T*R`jM4"IJ)PXp6JVMiui{LMAO3Ll㫍&0MP)I$N\QЭJMUDU ĴK Eu4nWU(W&jR,e*Ȏ2VBӆg*$ѓ+9FQǑ7;px*y$)%dC|%{VJJDjp-۵JRUuE-U9TZGjv\3U(Su%:n:vbm[+up`iIeo7ܥx:Sܝ+ه ʍ8^U֤ҹ9bYxl¡iCFWel5(JRDH&2h6K.5K TfˣT\mcɦ6Su{1UceiyV'EBa;ڈMT5Epvjw rQ+eZ2wrǑZ Ӡ;攫ᆕَxRҢEʖkXdT $*52"e!?mܒu*# hJGaDNIY +R**StCqP$bTJ*# 4QR$%CT0%HT)TTT ,8HY*E*)NȇtTum#!$I84?$aNزw胤Du`tTB/dD~sRv(dpCd><7G}׉,>kEĹ4Pk1|a&/ˈp0zayCUZ7a.ue+daN¦1!]'ҮWreupj5uNj\Y_U1*cV+]*4lԲDj[ EċXghjUR7u\fNʦw0-Jrڴׅ*#d2EV"QꢭUIUvP_Z,R:*ꪪ'TY%u4J"cg 8`moV[y6ӣ:0F ޢVUm[m[V-[w+brbyI E{RiROZy*DV,'!6TܝDir|oah|Gi=4ZI|ȉߜD nꎅ URk$C{SҭO]#Ht;;zt)#y,|ry+%. 7]I6lEUtX6cѱוPr k[JMچ,zd HƓ*f$!K!T Օ$QOή;6'r|aϕa;ڵ{_> DقP+Yrkv*ӵaªVZnɉ*8Tq`uIU۫RM%QTNy?U?ӗ'y#}lRȒ #1D9Y :WRބG'9/V %ED^Vʨu]bXDK+0)Ƙܦ]LN' )*4Hӫl*V%TdYu`%RRhIcº <8B!B(֭ jBDa8M1LMLVbF$VEEUkb1HҫCecMTi,*HT%*2&I&H˜–UQT%bQd`al&b&Uebdbъ~"ê;!Q%CNM%U%>DJZ8ZLiK1$*Ѕ-[%R]%D*~DGHKJ9rcO[~xޗRR,I zzwIM Q6j'UdǦ?{8%dE%%:v5ńKL `F&$a )UI20HȚQ3ETMF)1SK +Ic(*PUUF1*T\b8I4QvRv($ +IQ?&Ҝ0ԃ?imYJ°ąD)JJI$) ('r_(]CAYa6CDuI**Uj*U$R$UPUU*X%JUDUJ$*ԕURIT*VB!iU*TR**T-UXR*[ *d UIVDU)DJJ*R"P撡9(*8OT*Yd<2Rbc#I*I06Y*%HrJLrɄ!'fSGSb;IT)UQVIlHdU(%ETIUUT#n#mQćd(U+@7kSFI *$CAktk^-8#a5ǓFg* c$įG/!<NODGU#EJ?aMedOK>4Y)%'pV@bEEE"`["-"D1M|!IJԈj Bt&S+˜Q&Ѥલ RHw2 ,~"$`ұRbRc"Z 0&4U ӏGMaK*bdcU rO6M8Jl*JUTpdUMKni|&!;,IH#đfbzF$&D=缯đx~#D%IPTRR CQTpF(DBELY%TE"*DR 2'kO5}:lZHGћ'J$sI$ZWe|*&D'H 'bERU{ő#SC:I'i#̅n.iɢL}46dێdtV;LIĝrsJt1Jb9pel,Z.~lFLBrN;'EwQc]lUJ*\f̢̥UQVov*(@nJW1*Jd1"1b %BtD 55:U$zu#_}=ʪ:$z `aޜ)\ROi46afԐW޳ˇ \1IQG62c)eEQU r)~>'FTIY#!)>T%XVITKTJV"*h,H$JUDPdJT*UhaTQjU[e,"EQOba)#Q'_?XqO~IBeRXLR>2I%YX`V*F DDEKTV%T!eH[UH(E!aEAܧ|C`hC_{"H>sǵ^d+iSK EȪ8bH0$LQB{HIܝ4%TNRG(O?Rʕ]?x`*,F R1' =B&LBi#1"RIӡ"~UU%+42 SF5Ғ(VR'R|ҼUO4/ZjbUSjV 4"J*b *`(׍K@M:sw2~0~ a-R#`]NoX8jy>S(y>H[D{9D6E1b<0(Ԁ[#>}!)iU\ETb1)ɨ IbuQLA*pq hLmL$(Tm\Ip1p4}"$RiDw',RaLгɤxuFr\ 1J1u40F`ndaIQ%YdJ%1(jJRUXEu5ԪvY4/bRwh DE ꙁЊ1ň{ԇ"NN~ N'zHERMɜɢ`a)b)J3 LV,YyQRK%(Xk1,yZL!WkV,VLɄhl0UMI-X U%űIUJՉB-L0S 0LRT*)miKm%UTRIHP:rNR>"?v%J+HQG"HzD7UrUy0HʥTCVx ;HU*TT\z=\Y'Xҽ+=i'N$OQbA; d&U:Y܌"V1 閙ds ,1!c RʧXj#EMdFԆ*JivJdI2IP¬6hQbW,bR$*QJJ*ȿ<֏mechQ#g۶_lƎEȶMLWӊ( EVYiA=϶Wq-B%&CT'zZ%;'gԜH+%F@^%GĦI%ZVU{qSK^aI,D47{Q~Cb}tipSfhҥN>5\`Z.KzDry? ©>WR*JNiJoYIX +HL,M&)4H+tF`ʉ9Ct *$$I$8i`U4+ T~F>dFA܂ I?O4Oz41q(d*cM4b¹Iբc:Ia^"cL4SȨTbf&'*:BD{(dȚxN!3()kؤ t"r>'➙'bɢE,0,YNOJ u>F$|qZdct0I=cR L1,r1smQcK6öڝc&9mC4*MJ) TmEc&I1k4:΍#:EiGЕJJeC |Ԯ1I]U:ʝUXE(LVsjSE8VM%ye&a\ŶʒVd,!pltjTznoXlM 쑃sM3Ç&L!E`#&0fnҴ2UU$bqcQ4QH,Ah :3B1¢ccyeSf3jQvԎYl1JX8aV+c|Jٴ« \)QhiR `tImc2F,N2NtEď̩N$D0bUQ-,TRQTi'HҴ*bV5)eN01ZlƚpE[EDȘs)i$RƘ -dxLU*4ma0qKLQN0V%|OAQTt!HNr eMk<JĔm'Y2E;;<<' 8ʓ}c!4;'TyHU5GyGUJtLCTU%5I4"ҹT*It1DbT%Fh`ҕ mdÉ$5$9W ۅNbU%(J(JTTȏI`&4.]-bRQ<~# $D%-+(iil`EUSϝM8GF'JNmUU$"*,QQaJJ-EDEY)U)RUU,DTIDETI$L2HUyI"*4|q*&`&]itU|T)w&HJ!7 ޲<6̉VI!GԨĝI2dUT)BUI'AJ}SŹ/'QJD)QQ g>G' |2@hL8HDŽ 2_}蒾1"*C?8S&BhŐ, Qe%U RNJ$Cݐ&dITŒNz$\#jwI$W BEPQ.HޟnTʩ,ZY1I"I i'IǵxRGXAhDU} p!;h~c4hGAD6*Z*Q%*m>NEXE*U L#|H](j=f?(:,r$|a5$RL))IR*EO<$UO='҈J4EI Ytz$ݑf` *Dd"hmc%eB&I$}.~$]Bs7>>$b*RQRp&Caz۸YTK"E1D'q'B@=&I !\!qJA;jk w]JƚJV& ,La6SqjIa6SsLMS#Zȧ !Lh*bJ?TRF5+mOc$>?^{+L~SK XřUW6S|Op{,=uz=ob,UdY͒YF&Hu|ъ10OQ1!>$#J?!a$!H'D>ITU*:#Μ%j^pH}EOQܧgzyi.K1E9d~ZGH=$ǥБѩ2#I>TRi%jnȶI3&CߑK{_,bRQQB(l/{*a HОvWt0I9}~5IENqB@`<@C,nC$YlQRA4󤌐Jq1GT$F'NF!5Hz(]%LRu$RG$J{_QJDDVa&?u>p<J*C!U?1PJ>ԅ:ʋ7ᇩS␨'ab%$]m%Qd<Oke'8H X ԩ5ƤMS-IY%XUF))RQhR$JBD7SgJۊqplɣ{:z'{m8TynJ!B}N &) O'¬{Y$S'Q,>TƂϡNeI_4Mp*D}*ҕ(iJqnU-%$=uq0X$,"h rQ Fď̃}xbTȨ*EcQ~N $TUERH I*RR*Sq;D$$:N$*D0;j-LY9RHS畒C;<0}$}G9!0#&NTNrĥRJJ5&22 "Ő%(&bFU$Pidv hRZTT,UTF6HܓK`ܑRJK’*Xfr4EDيQy$Y$x'~^ N촤)SWM[HI6$f:;nrl{tDe'ZqDQVzDQ)EQڈ;T5l5hȱສTE:g]5'Enjw1,UMd~"DxؘGQT=,)'9KQe{ڑ1 O$|6M!J*ȓKXQTK!JRIdIKu!OT)L$jbii#M4w'Fr2b1-V5<64li:N$H8z}ݧȩ'KmVI teOci)J]P%,H*U$aRxI%,I*)HUiR|+"idމUb%C ([nD: d,D瑧 \0M"i| \BK! <XJX8QUY lE2Uv(\9իEjK4mXѾQdK,+-4ac6ѽ+`w+xAlwF+kLʳeUu(J]F*]0.ɘRĺqaզII˗HNT&1^#vQr.7UB!#e7- \F׳[S9n+LrWcce$JYQ*Z%D2eS* jʭr,]tv)t'eurS7UI]pw ᪛Fe#VtRG%wIN\<m㢓6bw]ÄE\QY;kEXiFNݣui0Γy4i[m4& 죕pafeU,Y,],*+Y(hî5sDh$FBU$ڜ"rmᦕ1HIJmX\V8p΅"RVU7aKV@u(Y0#v.5K)PLIo4^(.ˑA#KjlѲ 1٩UqF8= :%:EkNnlm,IPI5N+D;*WGUmIS^j 9MBHt's/( NvL4*R2#H6CK-bRwrU FIuI '(wQ"RFΣdQDs<K }JI}5'{z"}j!:SeE(NgOL,|c)ItJWL*:4*J֕MVV)%gi00nhF 8cJ+ +%R%T Sm0ҵDY *DTRF,wG*J{ʷ)b:*cLJM m+L ¤iБ|Je*jϴ쮐,$%I>JN9N]ʥrƈҕM 0ؚTLRQ0ԥ1bE%TUT0JbV0Opc"=(w?I0PGM"3,I%GCf4(y?$6 bĩI$~b#Eb>RJX)*UR*X*,TDФJ;ES$y0w$8 `,n$;㪪FU?y$"9fOdTv44i$=LHY!O]ċ"4j&45ҥT$uұZ&zm4l[iRQI@H5ROB'X*QD"3#E X1 bZG b)R"RUY a*Ju/r6|>U^E;$<:,U)H|'1VD,$G0bI7 &@HOQQܯXcRԊTI2H\z?J*T$1Y?1?~NΧzR,őUE%UmaaTJe*J~`RHzЇ~BzٵTRI;'d}_FFI"I>7ΩRUEUXUX/W }ğQdG9OCJeińڣ4[f2j*3UJtWE*^8b] նrq$مpW!M$jN"pZsZ֚MT+ I%)U,fF*U]0E.H4CƝQ791XT=? $|mf*VY8j$[4׬*%\ R Q]ڤ*d'l!(WEr=xˮ* U~9b4ci h@ÜXLÔ=O_??muvՓ ReeO #i`jcZMYfGܧǥXҪRUhF&GϢؑ/aӌu`01lj,@ZEA[MhJZF*eltS!>؊H*Jĥ9G 7ic$KS%ay-+G^Am:,xk&K:;}j.jڬe-S(XLl4z?)>Vf30E' ,E(~Z-4ǽ 4],ydx1sR6m0*5 Lg;&elUFIn%rRi `ي#Oh vFnrA?~ʙ]҇|/˝+ _*zaZJYQwTnGw+8J겈4{}U>Эں*Unql??l9g>gu&| KäR~{/$У7bˤV}2?%x1NZ^W F'5QB^o 'SI NSIR`E>_4Lỽ2gR;V6NKmY0-%̸^Y*I}B8S/I|f1-VH (-PStEKUI$ɫ;]MuZ,V.ٍJM6cg5rUǽs? fTJRJ?XYKea͗ ݣej-ݣv>s0G:-,qɪmbb^pGU#R`!/LŸ3~tiBEUHBJDIAk2UH=~7bzx3"hUCZM~ sސܖf5XX">0,#q=)\+ۖc1d*Ĵ`~a?tm("mlLG6mBe嶾,6E *0Xir4aC>ֿd5a1Uhddab5t̝a>5 $N|?)„K0jlint-3.0/types.hh0000644000175000017500000001121007643347427014732 0ustar davehodaveho00000000000000#ifndef TYPES_HH #define TYPES_HH #ifdef VISUAL_CPP #include #pragma warning( disable : 4786) #endif #include #include #include typedef int int4; typedef unsigned nat4; #if defined(__GNUC__) #define INT8_DEFINED 1 typedef long long int8; typedef unsigned long long nat8; #else #if defined(_WIN32) #define INT8_DEFINED 1 typedef __int64 int8; typedef unsigned __int64 nat8; #else #if defined(__osf__ ) #define INT8_DEFINED 1 typedef signed long int8; typedef unsigned long nat8; #endif #endif #endif #define bool int #define true 1 #define false 0 #define nobreak typedef unsigned char byte; typedef unsigned short word; enum vbm_instruction_code { #define JAVA_INSN(code, mnem, len) mnem, #include "jlint.d" last_insn }; #define items(array) (sizeof(array)/sizeof*(array)) enum message_category { cat_deadlock = 0x00000001, cat_race_condition = 0x00000002, cat_wait_nosync = 0x00000004, cat_synchronization = 0x0000000F, cat_super_finalize = 0x00000010, cat_not_overridden = 0x00000020, cat_field_redefined = 0x00000040, cat_shadow_local = 0x00000080, cat_inheritance = 0x000000F0, cat_zero_operand = 0x00000100, cat_zero_result = 0x00000200, cat_redundant = 0x00000400, cat_overflow = 0x00000800, cat_incomp_case = 0x00001000, cat_short_char_cmp = 0x00002000, cat_string_cmp = 0x00004000, cat_weak_cmp = 0x00008000, cat_domain = 0x00010000, cat_null_reference = 0x00020000, cat_truncation = 0x00040000, cat_bounds = 0x00080000, cat_data_flow = 0x000FFF00, cat_done = 0x10000000, cat_all = 0xFFFFFFFF }; enum type_tag { tp_bool, tp_byte, tp_char, tp_short, tp_int, tp_long, tp_float, tp_double, tp_void, tp_self, tp_string, tp_object }; struct int_type_range { int4 min; int4 max; }; struct message_descriptor { message_category category; const char* format; const char* name; bool position_dependent; bool enabled; }; enum message_code { #define MSG(category, code, pos, text) msg_##code, #include "jlint.msg" msg_last }; #define MSG_LOCATION_PREFIX "%0s:%1d: " // Emacs style: "file:line_number: " #define MAX_MSG_LENGTH 1024 #define MAX_MSG_PARAMETERS 16 const unsigned class_hash_table_size = 1987; struct msg_select_category_option { message_category msg_cat; const char* cat_name; const char* cat_desc; }; class field_desc; struct vbm_operand { int type; // type of expression/variable before it was pushed in stack int4 max; // maximal value of operand int4 min; // minimal value of operand int4 mask; // mask of possible set bits and zero value indicator for // object types int index; // index of local veriable, which value was loaded in stack const field_desc* equals; }; #define IS_INT_TYPE(tp) (tp <= tp_int) #define IS_ARRAY_TYPE(tp) ((tp & ~0xFF) != 0) int_type_range const ranges[] = { // min max {0x00000000, 0x00000001}, // tp_bool {0xffffff80, 0x0000007f}, // tp_byte {0x00000000, 0x0000ffff}, // tp_char {0xffff8000, 0x00007fff}, // tp_short {0x80000000, 0x7fffffff} // tp_int }; int const array_type[] = { 0, 0, 0, 0, tp_bool, tp_char, tp_float, tp_double, tp_byte, tp_short, tp_int, tp_long }; int const vbm_instruction_length[] = { #define JAVA_INSN(code, mnem, len) len, #include "jlint.d" 0 }; char const* const vbm_instruction_mnemonic[] = { #define JAVA_INSN(code, mnem, len) #mnem, #include "jlint.d" NULL }; #ifndef MAX_PATH #define MAX_PATH 1024 #endif #ifdef INT8_DEFINED #define TO_INT8(high, low) ((nat8(high) << 32) | unsigned(low)) #define LOW_PART(x) int4(x) #define HIGH_PART(x) int4(nat8(x) >> 32) #define LOAD_INT8(src,field) TO_INT8((src)[0].field, (src)[1].field) #define STORE_INT8(dst,field,src) (dst)[0].field = HIGH_PART(src),\ (dst)[1].field = LOW_PART(src) #ifndef INT8_MAX #define INT8_MAX ((int8)((nat8)-1 >> 1)) #endif #ifndef INT8_MIN #define INT8_MIN ((int8)(((nat8)-1 >> 1) + 1)) #endif #define INT8_ZERO ((int8)0) #define INT8_ALL_BITS ((int8)-1) #endif #define MAX_ARRAY_LENGTH 0x7fffffff #define ALL_BITS 0xffffffff #define SIGN_BIT 0x80000000 #define NO_ASSOC_VAR -1 #ifdef _WIN32 #define FILE_SEP '\\' #else #define FILE_SEP '/' #endif // declared in jlint.cc: extern int max_shown_paths; extern char* source_file_path; extern int source_file_path_len; extern bool source_path_redefined; extern int n_messages; #endif jlint-3.0/utf_string.hh0000644000175000017500000000474307653162217015760 0ustar davehodaveho00000000000000#ifndef UTF_STRING_HH #define UTF_STRING_HH #include #include #include #include "functions.hh" #include "types.hh" #include "message_node.hh" class method_desc; class class_desc; extern unsigned int string_hash_function(byte* p); class utf_string { protected: int len; // string length without trailing \0 byte* data; public: bool operator == (utf_string const& str) const { return len == str.len && memcmp(data, str.data, len) == 0; } bool operator != (utf_string const& str) const { return len != str.len || memcmp(data, str.data, len) != 0; } bool operator == (const char* str) const { return strcmp((char*)data, str) == 0; } bool operator != (const char* str) const { return strcmp((char*)data, str) != 0; } unsigned hash() const { return string_hash_function(data); } int first_char() const { return data[0]; } void operator = (utf_string const& str) { len = str.len; data = str.data; } utf_string operator + (const char* suffix) const { utf_string str; str.len = len + strlen(suffix); str.data = new byte[str.len+1]; memcpy(str.data, data, len); memcpy(str.data+len, suffix, str.len - len); str.data[str.len] = 0; // zero terminated string return str; } void append(int offs, utf_string const& suffix) { assert(offs <= len); len = offs + suffix.len; byte* new_data = new byte[len+1]; memcpy(new_data, data, offs); memcpy(new_data+offs, suffix.data, suffix.len); new_data[len] = 0; // zero terminated string delete[] data; data = new_data; } int rindex(byte ch) const { byte* p = (byte*)strrchr((char*)data, ch); return p ? p - data : -1; } void set_size(int size) { len = size; } const char* as_asciz() const { return (const char*)data; } utf_string(int length, byte* str) { len = length; data = new byte[length+1]; memcpy(data, str, length); data[length] = 0; } utf_string(const char* str) { len = strlen(str); data = (byte*)str; } utf_string(utf_string const& str) { len = str.len; data = str.data; } utf_string(utf_string const& str, bool foo) { len = str.len; data = str.data; if (FILE_SEP != '/') { // Produce valid operationg system dependent file name for (char* p = (char *) data; *p != '\0'; p++) { if (*p == '/') { *p = FILE_SEP; } } } } utf_string() { len = 0; data = NULL; } }; #endif jlint-3.0/var_desc.hh0000644000175000017500000000102207240344550015337 0ustar davehodaveho00000000000000#ifndef VAR_DESC_HH #define VAR_DESC_HH #include "utf_string.hh" class var_desc { public: utf_string name; int type; int start_pc; int4 min; int4 max; int4 mask; enum object_var_state { vs_unknown = 0x01, // state of variable is unknown vs_not_null = 0x03, // variable was checked for null vs_new = 0x04 // variable points to object created by new }; const field_desc* equals; var_desc() { equals = NULL; } }; #endif