dehydra-89ed48e70997/.hg_archival.txt0000644000000000000000000000022511757635752017253 0ustar rootroot00000000000000repo: de36c07be256d44ebf681911557068f1309b00c1 node: 89ed48e709978075d7918bf2c575835c771ec585 branch: default latesttag: null latesttagdistance: 561 dehydra-89ed48e70997/.deps/this_is_where_dependancy_info_goes0000644000000000000000000000007011757635752024175 0ustar rootroot00000000000000I don't want to run mkdir -p for every file compilation dehydra-89ed48e70997/COPYING0000644000000000000000000004312211757635752015223 0ustar rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU 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) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 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. dehydra-89ed48e70997/Makefile.in0000644000000000000000000000531611757635752016240 0ustar rootroot00000000000000# Dehydra and Treehydra scriptable static analysis tools # Copyright (C) 2007-2010 The Mozilla Foundation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. GCCSTAGE=gcc include config.mk INCLUDE = -DIN_GCC -DPLUGIN_NO_POISON -DHAVE_CONFIG_H $(GCC_PLUGIN_HEADERS) \ -I$(SM_INCLUDE) -I$(SM_LIBDIR) -I/$(HOME)/local/include/js/ #-I/$(HOME)/local/include/js/ CFLAGS= -Wall -fPIC -DXP_UNIX $(INCLUDE) $(CONFIGURE_CFLAGS) COMMON=dehydra.o util.o dehydra_types.o LDFLAGS=-L$(SM_LIBDIR) -Wl,-rpath,$(SM_LIBDIR) -l$(SM_NAME) $(SHARED_LINK_FLAGS) DEHYDRA_OBJS = dehydra_ast.o $(COMMON) TREEHYDRA_OBJS= treehydra.o treehydra_builtins.o treehydra_plugin.o treehydra_generated.o $(DEHYDRA_OBJS) DEPDIR = .deps all: gcc_dehydra.so gcc_treehydra.so gcc_dehydra.so: dehydra_plugin.o dehydra_builtins.o jsval_map.o $(DEHYDRA_OBJS) $(CXX) -o $@ $+ $(LDFLAGS) gcc_treehydra.so: gcc_dehydra.so $(TREEHYDRA_OBJS) jsval_map.o libs/useful_arrays.js $(CXX) -o $@ $(TREEHYDRA_OBJS) jsval_map.o $(LDFLAGS) -include $(TREEHYDRA_OBJS:%.o=$(DEPDIR)/%.P) %.o: %.c $(CC) -MD -g3 $(CFLAGS) -c $< @mkdir -p $(DEPDIR) @cp $*.d $(DEPDIR)/$*.P; \ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $(DEPDIR)/$*.P; \ rm -f $*.d treehydra_generated.c: gcc_cp_headers.h convert_tree.js gcc_dehydra.so $(CXX) -Wp,-C -save-temps -DTREEHYDRA_CONVERT_JS -fpermissive -fshow-column $(CFLAGS) -fplugin=./gcc_dehydra.so -fplugin-arg-gcc_dehydra-script=convert_tree.js $< -o /dev/null jsval_map.o: jsval_map.cc jsval_map.h $(CXX) -g3 $(CFLAGS) -c $< useful_arrays.ii: useful_arrays.c $(CC) $(INCLUDE) -E $< -o $@ libs/useful_arrays.js: useful_arrays.ii sed -e 's/.*tree_code/var tree_code/' -e 's/^#.*//' -e 's/\[\]//' -e 's/{/[/' -e 's/}/]/' $< > $@ treehydra: gcc_treehydra.so .PHONY: treehydra check check_dehydra check_treehydra check_both check_both_tinderbox: $(MAKE) -C test $@ clean: rm -f *.o *.so *~ *.s *.ii *_generated.c libs/enums.js libs/useful_arrays.js $(DEPDIR)/*.P $(MAKE) -C test clean distclean: clean rm -f Makefile config.mk dehydra-config.h dehydra-89ed48e70997/README0000644000000000000000000000174611757635752015056 0ustar rootroot00000000000000Dehydra and Treehydra scriptable static analysis tools Copyright (C) 2007-2010 The Mozilla Foundation See the homepage for more information: http://developer.mozilla.org/en/docs/Dehydra http://developer.mozilla.org/en/docs/Treehydra This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *** Build instructions *** See http://developer.mozilla.org/en/docs/Dehydra_GCC dehydra-89ed48e70997/configure0000755000000000000000000001456011757635752016103 0ustar rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # Dehydra and Treehydra scriptable static analysis tools # Copyright (C) 2007-2010 The Mozilla Foundation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Author: Taras Glek # OSX support by Vlad Sukhoy import getopt, sys, os.path, os, platform, subprocess, tempfile def usage(): print """ Usage: ./configure ... --js-name= part between lib and .so in libjs.so on your system (Usually js, mozjs on Ubuntu) --js-libs= Location of libjs.so --js-headers= SpiderMonkey headers, location of jsapi.h. Might get autodetected correctly from --js-libs. --enable-C Enable experimental C support""".lstrip() pass def error(msg): print >> sys.stderr, "Error: " + msg sys.exit(1) def checkdir(path,opt): print "Checking " + opt + ": " + path if not os.path.isdir (path): error("directory '"+path+"' doesn't exist, specify correct directory with --" + opt) def getjsdirs(jsname, jslibs, jsheaders): if jslibs == None: libjs = "/usr/lib/lib" + jsname + dynamic_lib_suffix if (os.path.isfile (libjs)): jslibs = os.path.dirname(libjs) print "Detected JavaScript library: " + libjs else: jslibs = os.path.realpath(jslibs) checkdir (jslibs, "js-libs") if jsheaders != None: checkdir (jsheaders, "js-headers") elif jslibs != None: jsapi = os.path.dirname(os.path.normpath(jslibs)) + "/include/" + jsname + "/jsapi.h" if (os.path.isfile (jsapi)): jsheaders = os.path.dirname(jsapi) print "Detected JavaScript headers: " + jsheaders return (jslibs, jsheaders) def detect_cxx(config): sys.stdout.write ("Checking for a C++ compiler: ") cxx="g++" try: cxx = os.environ["CXX"] except: pass config["CXX"] = cxx sys.stdout.write ("%s\n" % cxx) def try_enable_finish_decl(config): print("Checking for FINISH_DECL callback:") cxx = config["CXX"] file = tempfile.NamedTemporaryFile("w+b", -1, ".c") file.write( """#include "gcc-plugin.h" enum plugin_event p = PLUGIN_FINISH_DECL; """) file.flush() po = subprocess.Popen(cxx + " -c -o /dev/null " + config["GCC_PLUGIN_HEADERS"] + " " + file.name, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if po.wait() == 0: config['FINISH_DECL'] = "yes" print("FINISH_DECL callback enabled"); else: print("FINISH_DECL callback disabled"); file.close() def try_gcc_plugin_headers(config): cxx = config["CXX"] sys.stdout.write ("Checking for %s plugin headers: " % cxx) proc = subprocess.Popen(cxx + ' -print-file-name=', shell=True, stdout=subprocess.PIPE, close_fds=True) path = os.path.join(proc.stdout.read().strip(), 'plugin', 'include') if not os.path.exists(path): raise Exception(cxx + " doesn't have plugin headers!") path = os.path.abspath(path) config["GCC_PLUGIN_HEADERS"] = "-I%s" % (path) print(path) try_enable_finish_decl(config) if __name__ == "__main__": dynamic_lib_suffix, shared_link_flags = ('.so', '-shared') cflags = '' if platform.system() == 'Darwin' : dynamic_lib_suffix, shared_link_flags = ('.dylib', '-bundle -flat_namespace -undefined suppress') cflags += '-fnested-functions' try: opts, args = getopt.getopt(sys.argv[1:], "h", ["js-name=", "js-libs=", "js-headers=", "enable-C"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) jsname = "mozjs" jsnamels = [jsname, "js"] jslibs = None jsheaders = None Csupport = False for o, val in opts: if o == "--js-name": jsname = val jsnamels = [jsname] elif o == "--js-libs": jslibs = os.path.expanduser(val) elif o == "--js-headers": jsheaders = os.path.expanduser(val) elif o == "--enable-C": Csupport = True elif o in ("-h", "--help"): usage() exit(0) else: error("unhandled option " + o) #Detect js stuff for loop_jsname in jsnamels: before = [jslibs, jsheaders] jslibs, jsheaders = getjsdirs(loop_jsname, jslibs, jsheaders) jsname = loop_jsname if (jslibs != None and jsheaders != None) or [jslibs, jsheaders] != before: break if jslibs == None: error("Must indicate javascript library directory with --js-libs=") if jsheaders == None: error("Must indicate javascript header directory with --js-headers=") if os.environ.has_key("CFLAGS"): cflags += ' ' + os.environ["CFLAGS"] config =dict(SM_NAME=jsname, SM_SUFFIX=dynamic_lib_suffix, SM_INCLUDE=jsheaders, SM_LIBDIR=jslibs, SHARED_LINK_FLAGS=shared_link_flags, CONFIGURE_CFLAGS=cflags) if Csupport: config['C_SUPPORT'] = "yes" print("Experimental C testsuite is enabled"); else: print("Disabling C testsuite"); detect_cxx(config) try_gcc_plugin_headers(config) f = open("config.mk", "w") f.write("""# %s %s """ % (" ".join(["'" + a + "'" for a in sys.argv]), "\n".join([k + "=" + str(v) for k, v in config.iteritems()]))) f.close() f = open("dehydra-config.h", "w") f.write("""#ifndef DEHYDRA_CONFIG_H #define DEHYDRA_CONFIG_H %s #endif """ % ("\n".join(["#define CFG_%s \"%s\"" % (k,str(v).replace('"','\\"')) for k, v in config.iteritems()]))) f.close() if os.path.exists("Makefile"): os.unlink("Makefile") os.symlink ("Makefile.in", "Makefile") dehydra-89ed48e70997/convert_tree.js0000644000000000000000000005501111757635752017225 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include ("map.js") include ("platform.js") var files = {} function getLine(fname, line) { var fbody = files[fname] if (!fbody) files[fname] = fbody = read_file(fname).split("\n") return fbody[line - 1] } /* Unwrap type from typedefs */ function skipTypedefs (type) { while (type) { let next_type = type.typedef if (next_type) { type = next_type } else break; } return type } /* Skips pointers/typedefs down to underlying primitive/aggregate type */ function skipTypeWrappers (type) { while (type) { if (type.typedef) { let next_type = type.typedef /* Workaround: C frontend doesn't give names to structs declared with a typedef: * typedef struct {...} foo; * on the other hand, that makes life easier for getPrefix() */ if (!next_type.name && type.name) next_type.name = type.name type = next_type } else if (type.type) type = type.type else break; } return type } /* Used to toggle names, name depends on whether it's a toplevel scope or nested deeper */ function getVarName(isTopmost) { return isTopmost ? "topmost" : "var" } function Field (type, name, tag, isAddrOf, arrayLengthExpr, cast, isPrimitive, unionResolver) { this.type = type this.name = name this.tag = tag this.isAddrOf = isAddrOf this.arrayLengthExpr = arrayLengthExpr this.cast = cast this.isPrimitive = isPrimitive this.unionResolver = unionResolver } Field.prototype.toString = function () { return this.type + " " + this.name + " = " + this.tag } // different function levels const FN_STATIC = new EnumValue ("FN_STATIC") const FN_TOPMOST = new EnumValue ("FN_TOPMOST") const FN_NESTED = new EnumValue ("FN_NESTED") function Function (fn_level, name, params, bodyFunc, subFunctions) { this.name = name this.params = params this.bodyFunc = bodyFunc this.comment = "" this.type = "void" this.fn_level = fn_level this.subFunctions = subFunctions } Function.prototype.addComment = function (comment) { this.comment += " " + comment } Function.prototype.signature = function () { return (this.fn_level === FN_STATIC ? "static " : "") + this.type + " " + this.name + " " + this.params } Function.prototype.toString = function (indent, outerindent) { if (!indent) indent = " " if (!outerindent) outerindent = "" var str = outerindent + "// " + this.comment + "\n" + outerindent + this.signature() + " {\n"; var extra = "" if (this.subFunctions) extra = this.subFunctions.map (function(x) {return x.toString(indent+" ", indent)}).join("\n") str += indent + this.bodyFunc(extra, indent) + "\n"+ outerindent + "}\n"; return str } function Unit () { this.functions = [] // Preinitialized with funcs that are implemented in C this.functionNames = {lazy_tree_node:{}, lazy_gimple_statement_d:{}} this.guarded = new Map() this.enumValues = {} } const unit = new Unit(); Unit.prototype.hasFunction = function (name) { return this.functionNames.hasOwnProperty(name) } Unit.prototype.toString = function (indent) { if (!indent) indent = " " var forwards = this.functions.map (function (x) { return x.signature() }) forwards.push("") var str = forwards.join (";\n"); return ["#define GENERATED_C2JS 1", "#include \"treehydra_generated.h\"", str, this.functions.join ("\n")].join("\n") } Unit.prototype.addFunction = function (f) { this.functions.push (f) this.functionNames[f.name] = f } Unit.prototype.registerEnumValue = function (name, value) { this.enumValues[name] = value } Unit.prototype.registerEnum = function (enum_type) { for each (let m in enum_type.members) { this.registerEnumValue(m.name, m.value); } } Unit.prototype.saveEnums = function (fname) { var constants = ["// generated by convert_tree.js"] for (var name in this.enumValues) { constants.push ("const " + name + " = new EnumValue(\"" + name + "\", " + this.enumValues[name] + ")") } write_file (fname, constants.join("\n")) print ("Generated " + fname) } Unit.prototype.guard = function (key) { if (this.guarded.has(key)) return true; this.guarded.put(key) return false } function callGetExistingOrLazy (type, name, isAddrOf, cast, isPrimitive, isArrayItem, isTopmost) { const varDeref = getVarName(isTopmost) + "->" const lazy_call = "lazy_" + type if (isAddrOf && !isArrayItem) { var expr = "&" + varDeref + name if (unit.hasFunction(lazy_call)) { var func = "get_lazy"; return func + " (this, " + lazy_call + ", " + expr + ", obj, \"" + name + "\")" } // do lazy call directly return lazy_call + " (this, " + expr + ", dehydra_defineObjectProperty (this, obj, \"" + name + "\"))"; } var deref = isAddrOf ? "&" : ""; var index = isArrayItem ? "[i]" : ""; var dest = !isArrayItem ? "obj" : "destArray"; var propValue = !isArrayItem ? '"' + name + '"' : "buf"; if (!cast) cast = "" else cast = "(" + cast + ") " var expr = cast + deref + varDeref + name + index if (isPrimitive) { return "convert_" + type + "(this, " + dest + ", " + propValue + ", " + expr + ");"; } if (unit.hasFunction(lazy_call)) { return "get_existing_or_lazy (this, " + lazy_call + ", " + expr + ", " + dest + ", " + propValue + ")" } throw new Error("eager getexisting/orlazy not implemented: "+lazy_call); } function makeUnionBody (fields, isTopmost, indent) { var noTagLs = [] var ls = [] ls.push ("switch (code) {"); for each (var f in fields) { if (!f.tag) { noTagLs.push(f) continue } ls.push ("case " + f.tag + ":"); ls.push (" " + callGetExistingOrLazy (f.type, f.name, f.isAddrOf, f.cast, f.isPrimitive, false, isTopmost) + ";") ls.push (" break;") } ls.push ("default:") ls.push (" break;") ls.push ("}") for each (var f in noTagLs) { ls.push (callGetExistingOrLazy (f.type, f.name, f.isAddrOf, f.cast, f.isPrimitive, false, isTopmost) + ";") } return ls.join("\n" + indent) } function makeUnion (fields, type_name, type_code_name, subFunctions, fn_level) { const isTopmost = fn_level != FN_NESTED function bodyFunc (extra, indent) { return extra + "\n" + indent + makeUnionBody (fields, isTopmost, indent) } const fname = "convert_" + type_name + "_union" const params = "(struct Dehydra *this, " + type_code_name + " code, union " + type_name + " *" + getVarName (isTopmost) + ", struct JSObject *obj)" return new Function (fn_level, fname, params, bodyFunc, subFunctions) } function makeEnum (unit, fields, type_name, type_prefix, enum_inherit, fn_level) { var ls = [] const enumNames = {} ls.push ("jsval v;"); ls.push ("switch (var) {"); for each (var f in fields) { unit.registerEnumValue (f.name, f.value) if (enumNames[f.value]) continue; enumNames[f.value] = f.name ls.push ("case " + f.name + ":"); ls.push (" v = get_enum_value (this, \"" + f.name + "\");") ls.push (" break;") } ls.push ("default:") if (enum_inherit) { ls.push (" convert_" + enum_inherit + " (this, parent, propname, (enum " + enum_inherit +") var);"); ls.push (" return;"); } else { ls.push (' error( "Treehydra Implementation: generating dummy tree code object for unimplemented tree_code %s\\n", tree_code_name[var]);'); ls.push (" v = get_enum_value (this, \"LAST_AND_UNUSED_TREE_CODE\");") } ls.push ("}") ls.push ("dehydra_defineProperty (this, parent, propname, v);") function bodyFunc (extra, indent) { return extra + "\n" + indent + ls.join ("\n" + indent) } return new Function (fn_level, "convert_" + type_name, "(struct Dehydra *this, struct JSObject *parent, const char *propname, " + type_prefix + " " + type_name + " var)", bodyFunc); } /* F is a Field */ function callUnion (type, name, unionResolver, isTopmost, indent) { const obj = "obj_" + name var str = "struct JSObject *" + obj + " = dehydra_defineObjectProperty (this, obj, \"" + name + "\");\n" str += indent + "convert_" + type + "_union (this, " + unionResolver + ", " + "&" + getVarName(isTopmost) + "->" + name + ", " + obj + ")"; return str } function makeStructBody (fields, indent, isTopmost) { var ls = [] for (var i in fields) { var f = fields[i] if (f.unionResolver) { ls.push (callUnion (f.type, f.name, f.unionResolver, isTopmost, indent)) } else if (!f.arrayLengthExpr) { // Only structs need their addresses to be taken // Assume that that also means they are unique to the structure // containing them ls.push (callGetExistingOrLazy (f.type, f.name, f.isAddrOf, f.cast, f.isPrimitive, false, isTopmost)) } else { var lls = ["{", "size_t i;"] lls.push ("char buf[128];") lls.push ("const size_t len = " + f.arrayLengthExpr + ";") lls.push ("struct JSObject *destArray = dehydra_defineArrayProperty (this, obj, \"" + f.name + "\", len);") lls.push ("for (i = 0; i < len; i++) {"); lls.push (" sprintf (buf, \"%zd\", i);") lls.push (" " + callGetExistingOrLazy (f.type, f.name, f.isAddrOf, f.cast, f.isPrimitive, true, isTopmost) + ";") lls.push ("}") ls.push (lls.join("\n" + indent + " ") + "\n" + indent+ "}") } } return ls.join (";\n" + indent) + ";" } function makeStruct (fields, type_name, prefix, subFunctions, fn_level) { const isTopmost = fn_level != FN_NESTED function printer (extra, indent) { var type = prefix + " " + type_name const varName = getVarName (isTopmost) var body = "if (!void_var) return;\n" body += indent + type + " *" + varName + " = ("+ type + "*) void_var;\n"; body += extra + "\n" body += indent + "if (treehydra_debug)\n" body += indent + indent + "dehydra_defineStringProperty (this, obj, \"_struct_name\", \"" + type_name + "\");\n" return body + indent + makeStructBody (fields, indent, isTopmost) } var f = new Function (fn_level, "lazy_" + type_name, "(struct Dehydra *this, void* void_var, struct JSObject *obj)", printer, subFunctions) return f } function getPrefix (aggr) { /* if this is C FE, then we know what we want to know * aka no typedef...with C++ there might still be a need to use the typedef * that was eaten */ if (sys.frontend == "GNU C") return aggr.kind if (aggr.kind != "struct" && aggr.kind != "enum") return aggr.kind + " " var file = aggr.loc.file; var line = aggr.loc.line; for (var i = 0;i < 5;i++) { // first find the begining of struct ... {} part var str = getLine (file, line) var pos = str.indexOf (aggr.kind) if (pos == -1) { line--; continue } // capture multiline struct decls for (var x = 1; x <= 5; x++) { str += getLine(file, line + x); } //typedef struct struct_name..means have to use struct on var var bracePos = str.indexOf ("{", pos); var namePos = str.indexOf (aggr.name, pos); if (namePos != -1 && namePos < bracePos) break; //typedef struct{ is the opposite if (str.lastIndexOf ("typedef", pos) != -1) return "" break } return aggr.kind } function isPointer (type) { if (type.typedef) return isPointer (type.typedef) else if (type.isArray) return isPointer (type.type) return type.isPointer; } const tagRegexp = /tag\s*\("([A-Z0-9_]+)"\)/ // should probly combine attribute extraction into an eval-happy general solution function getUnionTag (attributes) { for each (var a in attributes) { if (a.name != "user") continue; var m = tagRegexp.exec(a.value) if (m) return m[1] } } const percentH_Regexp = /%h/g const percent0_Regexp = /%0/g const percent1_Regexp = /%1/g const descRegexp = /desc\s*\("(.*)"\)/; function getUnionResolver(attributes, fieldName, isTopmost) { for each (var a in attributes) { if (a.name != "user") continue; var m = descRegexp.exec(a.value) // took a guess about %h in this context if (m) { const varName = getVarName(isTopmost) var str = m[1].replace(percent0_Regexp, "(*topmost)") str = str.replace(percent1_Regexp, "(*" + varName + ")") str = str.replace(percentH_Regexp, varName + "->" + fieldName) return str } } } const lengthRegexp = /length\s*\(\s*"(.+)"\s*\)/; function getLengthExpr (attributes, isTopmost) { for each (var a in attributes) { if (a.name != "user") continue; var m = lengthRegexp.exec (a.value); if (m) return m[1].replace(percentH_Regexp, "(*" + getVarName(isTopmost) + ")") } } const specialRegexp = /special/ function isSpecial (attributes) { for each (var a in attributes) { if (a.name != "user") continue; if (specialRegexp.test(a.value)) return true } } const skipRegexp = /skip/ function isSkip (attributes) { for each (var a in attributes) { if (a.name != "user") continue; if (skipRegexp.test(a.value)) return true } } const charRegexp = /char/ function isCharStar (type) { while (type.typedef) type = type.typedef if (!(type.isPointer || (type.isArray && type.max == "0u"))) return false type = type.type while (type.typedef) type = type.typedef return type.name && charRegexp.test(type.name) } const unsignedIntRegexp = /unsigned|int/ function isUnsignedOrInt (type) { while (type.typedef) type = type.typedef return type.name && unsignedIntRegexp.test (type.name) } const stripPrefixRegexp = /([^:]+)$/; const arraySizeRegexp = /^(\d)u$/; const location_tRegexp = /location_t|location_s|source_locus/; /* meaty part of the script * Unit is what holds the result * Aggr is the data type to convert * zeroTh(%0), is the outermost(ie outmost by value struct) aggr type * first(%1) is the parent of current type * byValue means that this aggr is part of another struct */ const tree_code_name = "tree_base::code"; function convert (unit, aggr, byValue) { if (!aggr || unit.guard(aggr)) { return } var ls = [] const isUnion = aggr.kind == "union" const isEnum = aggr.kind == "enum" // isToplevelType is basically needed for %1/%0/%h stuff // indicates that reflection for the type should be nested // TODO: get rid of the ":" clause, // marking types similar to isGtyPercent0 should be enough const isToplevelType = aggr.name.indexOf(":") == -1 && (!byValue || !aggr.isGtyPercent0) const aggr_name = stripPrefixRegexp.exec(aggr.name)[1] // Keep nested structs/unions here var subFunctions = [] var aggr_ls = undefined if (!isEnum) aggr_ls = aggr.members var oldloc = this._loc for each (var m in aggr_ls) { this._loc = m.loc var name = stripPrefixRegexp.exec(m.name)[1] var type = skipTypeWrappers (m.type) var type_name = stripPrefixRegexp.exec(type.name)[1] var isAddrOf = false var type_kind = type.kind var tag = undefined var lengthExpr = undefined /* Extract size if type is an array */ var lengthResults = arraySizeRegexp.exec (m.type.max); var unionResolver = undefined var subf = undefined /* arrays of size 1 are actually funky var length arrays */ if (lengthResults && lengthResults[1] != "u") lengthExpr = lengthResults[1]*1 + 1 var cast = undefined // when an enum is used as a bitfield, then gcc calls it an unsigned // :(..restore enum info here if (m.name == tree_code_name) { type_kind = "enum" type = this.tree_code type_name = type.name } else if (m.name == "gimple_statement_base::code") { type_kind = "enum" type = this.gimple_code type_name = type.name } switch (m.name) { case "tree_type::symtab": case "emit_status::x_regno_reg_rtx": case "ssa_use_operand_d::use": print ("Skipping " + m.name + " because it causes issues I don't feel like dealing with") continue; default: if (m.name != "lang_type::lang_type_u::h" && (isSkip(m.attributes) || isSkip(m.type.attributes))) { print ("Harmless: skipping " + m.name + ". "); continue; } } var isPrimitive = false if (isUnion) { // GTY tags help figure out which field of a union to use tag = getUnionTag (m.attributes); if (!tag) tag = getUnionTag (m.type.attributes) } if (type_kind == "struct" // Allow anonymous unions and 2 whitelisted unions || (type_kind == "union" && ((type.name == "tree_node" || type.name == "gimple_statement_d") || type.name.indexOf("::") != -1))) { isAddrOf = !isPointer(m.type) if (type.isIncomplete) { print ("Harmless: "+m.name + "' type is incomplete"); continue; } // taras: There doesnt seem to be a convenient place to specificy handcoded // struct conversion functions. if (type.name != "tree_string") { let isInplaceStruct = skipTypeWrappers(m.type) == type; subf = convert (unit, type, isInplaceStruct) if (!isUnion) { lengthExpr = getLengthExpr (m.attributes, isToplevelType) if (type.name != "tree_node") unionResolver = getUnionResolver (m.type.attributes, name, isToplevelType) } } } else if (type_kind == "enum") { isPrimitive = true subf = convert (unit, type) } else if (isCharStar (m.type)) { type_name = "char_star" cast = "char *" isPrimitive = true lengthExpr = undefined } else if (location_tRegexp.exec(m.type.name)) { // This must appear before isUnsignedOrInt because location_t is an // int (if we are not using gcc 4.2), but we want to convert it to a // formatted location. type_name = 'location_t'; cast = 'location_t'; isPrimitive = true; } else if (isUnsignedOrInt(m.type)) { type_name = "int" cast = "HOST_WIDE_INT" isPrimitive = true } else { print("Likely harmless: unhandled " +type_kind+" "+ type_name + " " + m.name + " " + m.loc) continue } if (isSpecial (m.attributes)) { if (m.name == "tree_exp::operands") lengthExpr = "TREE_OPERAND_LENGTH ((tree) &(*topmost))"; else { print (m.name + " is special. Skipping...") continue } } ls.push (new Field (type_name, name, tag, isAddrOf, lengthExpr, cast, isPrimitive, unionResolver)) if (subf) subFunctions.push(subf) } this._loc = oldloc var ret = undefined if (isUnion) { ret = makeUnion (ls, aggr_name, (aggr_name == "tree_node" ? "enum tree_node_structure_enum" : "unsigned int"), subFunctions, isToplevelType ? FN_TOPMOST : FN_NESTED) } else if (aggr.kind == "struct") { let level switch(aggr_name) { case "cgraph_node": case "tree_common": level = FN_TOPMOST break; default: level = isToplevelType ? FN_STATIC : FN_NESTED; } ret = makeStruct (ls, aggr_name, getPrefix(aggr), subFunctions, level) } else if (isEnum) { var enum_inherit = undefined if (aggr_name == "tree_code") { // do inheritance stuff convert (unit, this.cplus_tree_code) enum_inherit = this.cplus_tree_code.name } ret = makeEnum (unit, aggr.members, aggr_name, getPrefix(aggr), enum_inherit, isToplevelType ? FN_STATIC : FN_NESTED) } if (ret) { ret.addComment (aggr.loc) if (!isToplevelType) return ret // don't add nested functions to toplevel unit.addFunction (ret); } } function isAttrTainted(attributes, taintCheck) { for each (var a in attributes) { if (a.name != "user") continue; if (taintCheck.exec(a.value)) { return true; } } } // Make use of DFS traversal to catch nested types that make use of %0 GTY function process_type_percent0(type) { if (isAttrTainted(type.attributes, percent0_Regexp)) { type.isGtyPercent0 = true; print(type.name + " is %0 tainted"); return; } if (type.kind != "union") for each (let m in type.members) { if (!m.type) continue let t = skipTypedefs(m.type); if (t.isGtyPercent0) { type.isGtyPercent0 = true; return } } } function process_type(type) { process_type_percent0(type); while (type.variantOf) type = type.variantOf if (type.name == "tree_code") { // needed because doing enumType foo:1 // makes gcc loose enum info in the bitfield this.tree_code = type } else if (type.name == "cgraph_node") { if (!type.attributes) { throw new Error (type.name + " doesn't have attributes defined. GTY as attribute stuff must be busted.") } this.cgraph_node = type } else if (type.name == "tree_code_class") { this.tree_code_class = type } else if (type.name == "cplus_tree_code") { this.cplus_tree_code = type } else if (type.name == "integer_type_kind") { this.integer_type_kind = type } else if (type.name == "gimple_code") { // make gimple_code a soft dependency to keep working with 4.3 this.gimple_code = type } else return // all of the enums mentioned above, should be registered if (type.kind == "enum") unit.registerEnum (type); } function input_end () { // got all the ingradients, time to cook if (!(this.tree_code_class && this.tree_code && this.cgraph_node && this.integer_type_kind)) { print ("Dehydra didn't find required types needed to generate Treehydra") return } if (!this.cplus_tree_code) this.cplus_tree_code = this.tree_code convert(unit, this.cgraph_node) var str = unit.toString() var fname = "treehydra_generated.c"; write_file (fname, str) print ("Generated " + fname) unit.saveEnums ("libs/enums.js") delete this.tree_code_class delete this.cgraph_node delete this.tree_code } dehydra-89ed48e70997/cp-tree-jsapi-workaround.h0000644000000000000000000000273111757635752021176 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* A workaround for weird conflict between Apple GCC 4.2 Preview 1's cp-tree.h and SpiderMonkey's jsotypes.h */ #if !defined(TREEHYDRA_CONVERT_JS) && !defined(TREEHYDRA_GENERATED) // do not include JS in special and therefore fragile files of treehydra #include #endif #ifdef JSVAL_OBJECT // workaround only when JS was actually included #ifndef uint32 #define DEHYDRA_DEFINED_uint32 #define uint32 uint32 #endif #endif // JSVAL_OBJECT #include #ifdef JSVAL_OBJECT #ifdef DEHYDRA_DEFINED_uint32 #undef uint32 #undef DEHYDRA_DEFINED_uint32 #endif #endif // JSVAL_OBJECT dehydra-89ed48e70997/dehydra.c0000644000000000000000000006755111757635752015770 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dehydra-config.h" #include #include #include #include #include #include #include #include #include "cp-tree-jsapi-workaround.h" #include #include #include #include #include #include "xassert.h" #include "dehydra_builtins.h" #include "util.h" #include "dehydra.h" #include "dehydra_types.h" #include "dehydra_ast.h" #ifndef JS_HAS_NEW_GLOBAL_OBJECT #define JS_NewCompartmentAndGlobalObject(cx, cl, pr) JS_NewObject(cx, cl, NULL, NULL) #endif const char *NAME = "name"; const char *SHORTNAME = "shortName"; const char *LOC = "loc"; const char *BASES = "bases"; const char *ASSIGN = "assign"; const char *VALUE = "value"; const char *TYPE = "type"; const char *FUNCTION = "isFunction"; const char *RETURN = "isReturn"; const char *FCALL = "isFcall"; const char *ARGUMENTS = "arguments"; const char *DH_CONSTRUCTOR = "isConstructor"; const char *DH_EXPLICIT = "isExplicit"; const char *FIELD_OF = "fieldOf"; const char *MEMBERS = "members"; const char *PARAMETERS = "parameters"; const char *ATTRIBUTES = "attributes"; const char *STATEMENTS = "statements"; const char *BITFIELD = "bitfieldBits"; const char *MEMBER_OF = "memberOf"; const char *UNSIGNED = "isUnsigned"; const char *SIGNED = "isSigned"; const char *MIN_VALUE = "min"; const char *MAX_VALUE = "max"; const char *PRECISION = "precision"; const char *HAS_DEFAULT = "hasDefault"; const char *TEMPLATE = "template"; const char *SYS = "sys"; static const char *STATIC = "isStatic"; static const char *VIRTUAL = "isVirtual"; static const char *INCLUDE_PATH = "include_path"; static const char *STD_INCLUDE = "libs"; static const char *VERSION_STRING = "gcc_version"; static const char *FRONTEND = "frontend"; static const char *EXTERN = "isExtern"; static const char *EXTERN_C = "isExternC"; static char *my_dirname (char *path); JSClass js_type_class = { "DehydraType", /* name */ JSCLASS_CONSTRUCT_PROTOTYPE, /* flags */ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static JSClass js_decl_class = { "DehydraDecl", /* name */ JSCLASS_CONSTRUCT_PROTOTYPE, /* flags */ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static JSClass global_class = { "DehydraGlobal", /* name */ JSCLASS_GLOBAL_FLAGS, /* flags */ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; void dehydra_init(Dehydra *this, const char *file, const char *version) { static JSFunctionSpec shell_functions[] = { DH_JS_FN("_print", Print, 0, 0, 0), DH_JS_FN("include", Include, 1, 0, 0), DH_JS_FN("write_file", WriteFile, 1, 0, 0), DH_JS_FN("read_file", ReadFile, 1, 0, 0), DH_JS_FN("diagnostic", Diagnostic, 0, 0, 0), DH_JS_FN("require", Require, 1, 0, 0), DH_JS_FN("hashcode", Hashcode, 1, 0, 0), DH_JS_FN("resolve_path", ResolvePath, 1, 0, 0), JS_FS_END }; this->fndeclMap = pointer_map_create (); this->rt = JS_NewRuntime (512L * 1024L * 1024L); if (this->rt == NULL) exit(1); this->cx = JS_NewContext (this->rt, 8192); if (this->cx == NULL) exit(1); #ifdef JSOPTION_METHODJIT JS_SetOptions(this->cx, JS_GetOptions(this->cx) | JSOPTION_JIT | JSOPTION_METHODJIT); #elif defined(JSOPTION_JIT) JS_SetOptions(this->cx, JS_GetOptions(this->cx) | JSOPTION_JIT); #endif JS_SetContextPrivate (this->cx, this); this->globalObj = JS_NewCompartmentAndGlobalObject (this->cx, &global_class, NULL); if (this->globalObj == NULL) exit(1); JS_InitStandardClasses (this->cx, this->globalObj); /* register error handler */ JS_SetErrorReporter (this->cx, ErrorReporter); xassert (JS_DefineFunctions (this->cx, this->globalObj, shell_functions)); if (dehydra_getToplevelFunction(this, "include") == JSVAL_VOID) { fprintf (stderr, "Your version of spidermonkey has broken JS_DefineFunctions, upgrade it or ./configure with another version\n"); exit(1); } this->rootedArgDestArray = JS_NewArrayObject (this->cx, 0, NULL); JS_AddObjectRoot (this->cx, &this->rootedArgDestArray); // this is to be added at function_decl time this->rootedFreeArray = JS_NewArrayObject (this->cx, 0, NULL); JS_DefineElement (this->cx, this->rootedArgDestArray, 0, OBJECT_TO_JSVAL (this->rootedFreeArray), NULL, NULL, JSPROP_ENUMERATE); JS_SetVersion (this->cx, JSVERSION_LATEST); /* Initialize namespace for plugin system stuff. */ JSObject *sys = dehydra_defineObjectProperty (this, this->globalObj, SYS); /* Set version info */ dehydra_defineStringProperty (this, sys, VERSION_STRING, version); dehydra_defineStringProperty (this, sys, FRONTEND, lang_hooks.name); /* Initialize include path. */ dehydra_defineArrayProperty (this, sys, INCLUDE_PATH, 0); char *filename_copy = xstrdup(file); char *dir = my_dirname(filename_copy); char *libdir = xmalloc(strlen(dir) + strlen(STD_INCLUDE) + 2); sprintf(libdir, "%s/%s", dir, STD_INCLUDE); dehydra_appendToPath(this, libdir); free(libdir); free(filename_copy); xassert (JS_InitClass(this->cx, this->globalObj, NULL ,&js_type_class , NULL, 0, NULL, NULL, NULL, NULL)); xassert (JS_InitClass(this->cx, this->globalObj, NULL ,&js_decl_class , NULL, 0, NULL, NULL, NULL, NULL)); } void dehydra_setFilename(Dehydra *this) { if (aux_base_name) { /* provide filename info */ jsval sys_val; JS_GetProperty(this->cx, this->globalObj, SYS, &sys_val); dehydra_defineStringProperty (this, JSVAL_TO_OBJECT(sys_val), "aux_base_name", aux_base_name); } if (main_input_filename) { /* provide filename info */ jsval sys_val; JS_GetProperty(this->cx, this->globalObj, SYS, &sys_val); dehydra_defineStringProperty (this, JSVAL_TO_OBJECT(sys_val), "main_input_filename", main_input_filename); } } int dehydra_startup (Dehydra *this) { return dehydra_includeScript (this, "dehydra.js"); } int dehydra_includeScript (Dehydra *this, const char *script) { jsval strval = STRING_TO_JSVAL (JS_NewStringCopyZ (this->cx, script)); int key = dehydra_rootObject (this, strval); jsval rval; int ret = !JS_CallFunctionName(this->cx, this->globalObj, "include", 1, &strval, &rval); dehydra_unrootObject (this, key); return ret; } JSObject *dehydra_getIncludePath (Dehydra *this) { jsval sys_val, path_val; JS_GetProperty(this->cx, this->globalObj, SYS, &sys_val); JS_GetProperty(this->cx, JSVAL_TO_OBJECT(sys_val), INCLUDE_PATH, &path_val); return JSVAL_TO_OBJECT(path_val); } /* Append a directory name to the script include path. */ void dehydra_appendToPath (Dehydra *this, const char *dir) { JSObject *path = dehydra_getIncludePath(this); unsigned int length = dehydra_getArrayLength(this, path); JSString *dir_str = JS_NewStringCopyZ(this->cx, dir); jsval dir_val = STRING_TO_JSVAL(dir_str); JS_DefineElement(this->cx, path, length, dir_val, NULL, NULL, JSPROP_ENUMERATE); } /* Avoiding bug 431100. Spec from man 2 dirname */ static char *my_dirname (char *path) { char *r = strrchr(path, '/'); if (!r) { strcpy (path, "."); return path; } else if (r == path && r[1] == 0) { return path; // '/' } else if (r[1] == 0) { // /foo/ foo/ cases *r = 0; return my_dirname (path); } *r = 0; return path; } /* Append the dirname of the given file to the script include path. */ void dehydra_appendDirnameToPath (Dehydra *this, const char *filename) { char *filename_copy = xstrdup(filename); char *dir = my_dirname(filename_copy); dehydra_appendToPath(this, dir); free(filename_copy); } /* Search the include path for a file matching the given name. The current * directory will be searched last. */ FILE *dehydra_searchPath (Dehydra *this, const char *filename, char **realname) { if (filename && filename[0] != '/') { JSObject *path = dehydra_getIncludePath(this); int length = dehydra_getArrayLength(this, path); int i; for (i = 0; i < length; ++i) { jsval val; JS_GetElement(this->cx, path, i, &val); JSString *dir_str = JS_ValueToString(this->cx, val); if (!dir_str) continue; char *dir = JS_EncodeString(this->cx, dir_str); xassert(dir); char *buf = xmalloc(strlen(dir) + strlen(filename) + 2); /* Doing a little extra work here to get rid of unneeded '/'. */ const char *sep = dir[strlen(dir)-1] == '/' ? "" : "/"; sprintf(buf, "%s%s%s", dir, sep, filename); JS_free(this->cx, dir); FILE *f = fopen(buf, "r"); if (f) { *realname = buf; return f; } else { free(buf); } } } FILE *f = fopen(filename, "r"); if (f) { *realname = xstrdup(filename); return f; } return NULL; } jsuint dehydra_getArrayLength (Dehydra *this, JSObject *array) { jsuint length = 0; xassert (JS_GetArrayLength (this->cx, array, &length)); return length; } JSObject *definePropertyObject (JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, JSObject *proto, uintN flags) { JSObject *nobj = JS_NewObject (cx, clasp, proto, NULL); JS_DefineProperty (cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, flags); return nobj; } void dehydra_defineProperty (Dehydra *this, JSObject *obj, char const *name, jsval value) { JS_DefineProperty (this->cx, obj, name, value, NULL, NULL, JSPROP_ENUMERATE); } jsval dehydra_defineStringProperty (Dehydra *this, JSObject *obj, char const *name, char const *value) { JSString *str = JS_NewStringCopyZ (this->cx, value); jsval val = STRING_TO_JSVAL(str); dehydra_defineProperty (this, obj, name, val); return val; } JSObject *dehydra_defineArrayProperty (Dehydra *this, JSObject *obj, char const *name, int length) { JSObject *destArray = JS_NewArrayObject (this->cx, length, NULL); dehydra_defineProperty (this, obj, name, OBJECT_TO_JSVAL (destArray)); return destArray; } JSObject *dehydra_defineObjectProperty (Dehydra *this, JSObject *obj, char const *name) { return definePropertyObject( this->cx, obj, name, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); } /* Load and execute a Javascript file. * Return: 0 on success * 1 on failure if a Javascript exception is pending * does not return if a Javascript error is reported * The general behavior of (De|Tree)hydra is to print a message and * exit if a JS error is reported at the top level. But if this function * is called from JS, then the JS_* functions will instead set an * exception, which will be propgated back up to the callers for * eventual handling or exit. */ jsval dehydra_getToplevelFunction(Dehydra *this, char const *name) { jsval val = JSVAL_VOID; return (JS_GetProperty(this->cx, this->globalObj, name, &val) && val != JSVAL_VOID && JS_TypeOfValue(this->cx, val) == JSTYPE_FUNCTION) ? val : JSVAL_VOID; } void dehydra_setLoc(Dehydra *this, JSObject *obj, tree t) { location_t loc = location_of (t); /* Don't attach empty locations */ if (loc_is_unknown(loc)) return; convert_location_t(this, obj, LOC, loc); } #ifndef __APPLE__ /* On modern GCCs we use a lazy location system to save memory and location lookup*/ static JSBool ResolveLocation (JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { *objp = obj; JSBool has_prop; // check if we already unpacked the location JSBool rv = JS_HasProperty(cx, obj, "file", &has_prop); if (rv && has_prop) return JS_TRUE; jsval v; JS_GetProperty (cx, obj, "_source_location", &v); location_t loc = JSVAL_TO_INT (v); expanded_location eloc = expand_location(loc); Dehydra *this = JS_GetContextPrivate(cx); dehydra_defineStringProperty (this, obj, "file", eloc.file); dehydra_defineProperty (this, obj, "line", INT_TO_JSVAL(eloc.line)); dehydra_defineProperty (this, obj, "column", INT_TO_JSVAL(eloc.column)); return JS_TRUE; } #endif static JSClass js_location_class = { "Location", /* name */ #ifndef __APPLE__ JSCLASS_NEW_RESOLVE, /* flags */ #else 0, #endif JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, #ifndef __APPLE__ (JSResolveOp) ResolveLocation, #else JS_ResolveStub, #endif JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; // Used by both Dehydra and Treehydra. void convert_location_t (struct Dehydra *this, struct JSObject *parent, const char *propname, location_t loc) { if (loc_is_unknown(loc)) { dehydra_defineProperty (this, parent, propname, JSVAL_VOID); return; } // Instead of calling ConstructWithArguments, we simply create the // object and set up the properties because GC rooting is a lot // easier this way. JSObject *obj = definePropertyObject(this->cx, parent, propname, &js_location_class, NULL, JSPROP_ENUMERATE); #ifdef __APPLE__ expanded_location eloc = expand_location(loc); dehydra_defineStringProperty (this, obj, "file", eloc.file); dehydra_defineProperty (this, obj, "line", INT_TO_JSVAL(eloc.line)); #else dehydra_defineProperty (this, obj, "_source_location", INT_TO_JSVAL(loc)); #endif } void dehydra_addAttributes (Dehydra *this, JSObject *destArray, tree attributes) { int i = 0; tree a; for (a = attributes; a; a = TREE_CHAIN (a)) { tree name = TREE_PURPOSE (a); tree args = TREE_VALUE (a); JSObject *obj = JS_NewObject(this->cx, NULL, 0, 0); JS_DefineElement (this->cx, destArray, i++, OBJECT_TO_JSVAL (obj), NULL, NULL, JSPROP_ENUMERATE); dehydra_defineStringProperty (this, obj, NAME, IDENTIFIER_POINTER (name)); JSObject *array = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, VALUE, OBJECT_TO_JSVAL (array)); int i = 0; for (; args; args = TREE_CHAIN (args)) { tree t = TREE_VALUE (args); const char *val = TREE_CODE (t) == STRING_CST ? TREE_STRING_POINTER (t) : expr_as_string (t, 0); JSString *str = JS_NewStringCopyZ(this->cx, val); JS_DefineElement(this->cx, array, i++, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE); } } } static void dehydra_setName (Dehydra *this, JSObject *obj, tree v) { if (DECL_NAME (v)) { tree n = DECL_NAME (v); jsval jsname = dehydra_defineStringProperty (this, obj, SHORTNAME, decl_as_string (n, 0)); // turns out calling decL_as_string on template names will cause template instantiation // avoid that like the plague as it can cause compiler bugs if (TREE_CODE(v) == TEMPLATE_DECL) dehydra_defineProperty (this, obj, NAME, jsname); else { if (HAS_DECL_ASSEMBLER_NAME_P (v)) dehydra_defineStringProperty (this, obj, "assemblerName", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (v))); dehydra_defineStringProperty (this, obj, NAME, decl_as_string (v, 0)); } } else { static char buf[128]; sprintf (buf, " _%d", DECL_UID (v)); switch (TREE_CODE (v)) { case CONST_DECL: buf[0] = 'C'; break; case RESULT_DECL: buf[0] = 'R'; break; default: buf[0] = 'D'; } dehydra_defineStringProperty (this, obj, NAME, buf); } } // Add the "hasDefault" property to function parameters with default values. // This information is provided by gcc as part of the function type definition, // so to add it to each parameter we must walk the parameter array and copy // the information. However, we must be careful to ignore the |this| // parameter for nonstatic class functions, which will be present in the // parameter array but not in the function type definition. void dehydra_moveDefaults (Dehydra *this, JSObject *obj) { jsval val; JS_GetProperty(this->cx, obj, TYPE, &val); if (val == JSVAL_VOID) return; JSObject *type_obj = JSVAL_TO_OBJECT(val); JS_GetProperty(this->cx, type_obj, HAS_DEFAULT, &val); if (val == JSVAL_VOID) return; JSObject *defaults_obj = JSVAL_TO_OBJECT(val); JS_GetProperty(this->cx, obj, PARAMETERS, &val); if (val == JSVAL_VOID) return; JSObject *params_obj = JSVAL_TO_OBJECT(val); jsuint defaults_length, params_length; JS_GetArrayLength(this->cx, defaults_obj, &defaults_length); JS_GetArrayLength(this->cx, params_obj, ¶ms_length); // determine if we have a nonstatic member function, which will // thus have a |this| parameter. |isStatic| hasn't been defined // on |obj| yet, so look at array lengths instead JS_GetProperty(this->cx, obj, MEMBER_OF, &val); int hasThis = val != JSVAL_VOID && params_length > defaults_length; int i; for (i = 0; i < defaults_length; ++i) { // offset the index into the array by one if |hasThis| JS_GetElement(this->cx, params_obj, i + hasThis, &val); JSObject *param_obj = JSVAL_TO_OBJECT(val); JS_GetElement(this->cx, defaults_obj, i, &val); if (val != JSVAL_VOID && JSVAL_TO_BOOLEAN(val)) dehydra_defineProperty(this, param_obj, HAS_DEFAULT, val); } } /* Add a Dehydra variable to the given parent array corresponding to * the GCC tree v, which must represent a declaration (e.g., v can * be a DECL_*. */ JSObject* dehydra_addVar (Dehydra *this, tree v, JSObject *parentArray) { if (!parentArray) parentArray = this->destArray; unsigned int length = dehydra_getArrayLength (this, parentArray); JSObject *obj = JS_NewObject (this->cx, &js_decl_class, NULL, this->globalObj); //append object to array(rooting it) JS_DefineElement (this->cx, parentArray, length, OBJECT_TO_JSVAL (obj), NULL, NULL, JSPROP_ENUMERATE); if (!v) return obj; /* Common case */ if (DECL_P(v)) { dehydra_setName (this, obj, v); tree decl_context = DECL_CONTEXT(v); if (decl_context && TREE_CODE(decl_context) == RECORD_TYPE) { dehydra_defineStringProperty (this, obj, ACCESS, TREE_PRIVATE (v) ? PRIVATE : (TREE_PROTECTED (v) ? PROTECTED : PUBLIC)); dehydra_defineProperty (this, obj, MEMBER_OF, dehydra_convert_type (this, decl_context)); } if (DECL_EXTERNAL(v)) { /* for non-static member variables, this attribute doesn't make sense */ if (TREE_CODE(v) != VAR_DECL || !decl_context || TREE_STATIC(v)) { dehydra_defineProperty (this, obj, EXTERN, JSVAL_TRUE); } } /* define the C linkage */ if ((TREE_CODE (v) == FUNCTION_DECL || TREE_CODE (v) == VAR_DECL) && DECL_EXTERN_C_P(v)) { dehydra_defineProperty(this, obj, EXTERN_C, JSVAL_TRUE); } tree typ = TREE_TYPE (v); if (TREE_CODE (v) == FUNCTION_DECL || (isGPlusPlus() && DECL_FUNCTION_TEMPLATE_P (v))) { dehydra_defineProperty (this, obj, FUNCTION, JSVAL_TRUE); if (isGPlusPlus() && DECL_CONSTRUCTOR_P (v)) { dehydra_defineProperty (this, obj, DH_CONSTRUCTOR, JSVAL_TRUE); if (DECL_NONCONVERTING_P (v)) { dehydra_defineProperty (this, obj, DH_EXPLICIT, JSVAL_TRUE); } } if (TREE_CODE(v) == FUNCTION_DECL) { JSObject *arglist = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, "parameters", OBJECT_TO_JSVAL (arglist)); tree args; for (args = DECL_ARGUMENTS(v); args; args = TREE_CHAIN (args)) dehydra_addVar(this, args, arglist); } if (isGPlusPlus()) { if (DECL_PURE_VIRTUAL_P (v)) dehydra_defineStringProperty (this, obj, VIRTUAL, "pure"); else if (DECL_VIRTUAL_P (v)) dehydra_defineProperty (this, obj, VIRTUAL, JSVAL_TRUE); } if (DECL_FUNCTION_TEMPLATE_P (v)) { tree args = DECL_INNERMOST_TEMPLATE_PARMS (v); int len = TREE_VEC_LENGTH (args); JSObject *template = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, TEMPLATE, OBJECT_TO_JSVAL (template)); int ix; for (ix = 0; ix != len; ++ix) { dehydra_addVar(this, TREE_VALUE(TREE_VEC_ELT(args, ix)), template); } } } dehydra_defineProperty (this, obj, TYPE, dehydra_convert_type (this, typ)); tree attributes = DECL_ATTRIBUTES (v); if (attributes) { JSObject *tmp = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, ATTRIBUTES, OBJECT_TO_JSVAL (tmp)); dehydra_addAttributes (this, tmp, attributes); } if (TREE_CODE (v) == FUNCTION_DECL) { // now we have the parameter list, pull hasDefault values from the type // spec and attach them to each individual parameter, for convenience dehydra_moveDefaults(this, obj); } /* Following code handles 3 different meanings of static */ /* static vars */ if ((TREE_CODE (v) == VAR_DECL && TREE_STATIC (v)) /* static functions */ || (TREE_CODE (v) == FUNCTION_DECL && !TREE_PUBLIC (v)) /* static class members */ || (TREE_CODE (TREE_TYPE(v)) == FUNCTION_TYPE && decl_context && TREE_CODE (decl_context) == RECORD_TYPE)) dehydra_defineProperty (this, obj, STATIC, JSVAL_TRUE); } else if (TREE_CODE(v) == CONSTRUCTOR) { /* Special case for this node type */ tree type = TREE_TYPE(v); char const *name = type_as_string(type, 0); dehydra_defineStringProperty(this, obj, NAME, name); dehydra_defineProperty(this, obj, DH_CONSTRUCTOR, JSVAL_TRUE); dehydra_defineProperty(this, obj, MEMBER_OF, dehydra_convert_type(this, type)); } else { /* Invalid argument tree code */ xassert(0); } dehydra_setLoc(this, obj, v); return obj; } int dehydra_visitType (Dehydra *this, tree t) { jsval process_type = dehydra_getToplevelFunction(this, "process_type"); if (process_type == JSVAL_VOID) return true; jsval rval, argv[1]; argv[0] = dehydra_convert_type (this, t); xassert (JS_CallFunctionValue (this->cx, this->globalObj, process_type, 1, argv, &rval)); return true; } static void dehydra_visitFunctionDecl (Dehydra *this, tree f) { jsval process_function = dehydra_getToplevelFunction (this, "process_function"); if (process_function == JSVAL_VOID) return; void **v = pointer_map_contains (this->fndeclMap, f); if (!v) { return; } if (!*v) { /* mustn't visit this function twice */ return; } int key = (int)(intptr_t) *v; this->statementHierarchyArray = JSVAL_TO_OBJECT (dehydra_getRootedObject (this, key)); *v = NULL; int fnkey = dehydra_getArrayLength (this, this->rootedArgDestArray); JSObject *fobj = dehydra_addVar (this, f, this->rootedArgDestArray); jsval rval, argv[2]; // This ensures that error/warning provide a sensible function name tree old_current_decl = current_function_decl; argv[0] = OBJECT_TO_JSVAL (fobj); argv[1] = OBJECT_TO_JSVAL (this->statementHierarchyArray); current_function_decl = f; xassert (JS_CallFunctionValue (this->cx, this->globalObj, process_function, sizeof (argv)/sizeof (argv[0]), argv, &rval)); current_function_decl = old_current_decl; dehydra_unrootObject (this, key); dehydra_unrootObject (this, fnkey); this->statementHierarchyArray = NULL; this->destArray = NULL; JS_MaybeGC(this->cx); } static void dehydra_visitVarDecl (Dehydra *this, tree d) { jsval process_decl = dehydra_getToplevelFunction (this, "process_decl"); if (process_decl == JSVAL_VOID) return; /* this is a hack,basically does dehydra_rootObject manually*/ int key = dehydra_getArrayLength (this, this->rootedArgDestArray); JSObject *obj = dehydra_addVar (this, d, this->rootedArgDestArray); jsval rval, argv[1]; argv[0] = OBJECT_TO_JSVAL (obj); xassert (JS_CallFunctionValue (this->cx, this->globalObj, process_decl, sizeof (argv)/sizeof (argv[0]), argv, &rval)); dehydra_unrootObject (this, key); } int dehydra_rootObject (Dehydra *this, jsval val) { /* positions start from 1 since rootedFreeArray is always the first element */ int pos; int length = dehydra_getArrayLength (this, this->rootedFreeArray); if (length) { jsval val; length--; JS_GetElement(this->cx, this->rootedFreeArray, length, &val); JS_SetArrayLength (this->cx, this->rootedFreeArray, length); pos = JSVAL_TO_INT (val); } else { pos = dehydra_getArrayLength (this, this->rootedArgDestArray); } xassert (pos != 0); /* don't overwrite the rootedFreeArray root */ JS_DefineElement (this->cx, this->rootedArgDestArray, pos, val, NULL, NULL, JSPROP_ENUMERATE); return pos; } void dehydra_unrootObject (Dehydra *this, int pos) { xassert (pos != 0); /* don't unroot the rootedFreeArray */ int length = dehydra_getArrayLength (this, this->rootedFreeArray); JS_DefineElement (this->cx, this->rootedFreeArray, length, INT_TO_JSVAL (pos), NULL, NULL, JSPROP_ENUMERATE); JS_DefineElement (this->cx, this->rootedArgDestArray, pos, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE); } jsval dehydra_getRootedObject (Dehydra *this, int pos) { jsval v = JSVAL_VOID; JS_GetElement(this->cx, this->rootedArgDestArray, pos, &v); return v; } void dehydra_visitDecl (Dehydra *this, tree d) { dehydra_visitVarDecl (this, d); if (TREE_CODE (d) == FUNCTION_DECL) { dehydra_visitFunctionDecl (this, d); } } void dehydra_print(Dehydra *this, jsval arg) { jsval print = dehydra_getToplevelFunction(this, "print"); if (print == JSVAL_VOID) { fprintf(stderr, "function user_print() not defined in JS\n"); return; } jsval rval; xassert (JS_CallFunctionValue(this->cx, this->globalObj, print, 1, &arg, &rval)); } void dehydra_input_end (Dehydra *this) { jsval input_end = dehydra_getToplevelFunction(this, "input_end"); if (input_end == JSVAL_VOID) return; jsval rval; xassert (JS_CallFunctionValue(this->cx, this->globalObj, input_end, 0, NULL, &rval)); JS_GC(this->cx); } dehydra-89ed48e70997/dehydra.h0000644000000000000000000001137211757635752015763 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DEHYDRA_H #define DEHYDRA_H #ifndef JS_TYPED_ROOTING_API #define JS_AddObjectRoot(cx, obj) JS_AddRoot(cx, obj) #define JS_AddStringRoot(cx, str) JS_AddRoot(cx, str) #define JS_AddNamedObjectRoot(cx, obj, name) JS_AddNamedRoot(cx, obj, name) #define JS_RemoveObjectRoot(cx, obj) JS_RemoveRoot(cx, obj) #define JS_RemoveStringRoot(cx, str) JS_RemoveRoot(cx, str) #endif struct Dehydra { JSRuntime *rt; JSContext *cx; JSObject *globalObj; JSObject *destArray; JSObject *rootedArgDestArray; JSObject *rootedFreeArray; // Where the statements go; JSObject *statementHierarchyArray; //keeps track of function decls to map gimplified ones to verbose ones struct pointer_map_t *fndeclMap; location_t loc; int inExpr; }; typedef struct Dehydra Dehydra; extern const char *NAME; extern const char *LOC; extern const char *BASES; extern const char *DECL; extern const char *ASSIGN; extern const char *VALUE; extern const char *TYPE; extern const char *FUNCTION; extern const char *RETURN; extern const char *FCALL; extern const char *ARGUMENTS; extern const char *DH_CONSTRUCTOR; extern const char *DH_EXPLICIT; extern const char *DH_EXTERN; extern const char *FIELD_OF; extern const char *MEMBERS; extern const char *PARAMETERS; extern const char *ATTRIBUTES; extern const char *STATEMENTS; extern const char *BITFIELD; extern const char *MEMBER_OF; extern const char *PRECISION; extern const char *UNSIGNED; extern const char *SIGNED; extern const char *MIN_VALUE; extern const char *MAX_VALUE; extern const char *HAS_DEFAULT; extern const char *TEMPLATE; extern const char *SYS; extern const char *ACCESS; extern const char *PUBLIC; extern const char *PRIVATE; extern const char *PROTECTED; extern JSClass js_type_class; /* Drop-in replacement for JS_DefineObject, required as a workaround * because JS_DefineObject always sets the parent property. */ JSObject *definePropertyObject (JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, JSObject *proto, uintN flags); jsuint dehydra_getArrayLength(Dehydra *this, JSObject *array); void dehydra_defineProperty(Dehydra *this, JSObject *obj, char const *name, jsval value); jsval dehydra_defineStringProperty(Dehydra *this, JSObject *obj, char const *name, char const *value); JSObject *dehydra_defineArrayProperty (Dehydra *this, JSObject *obj, char const *name, int length); JSObject *dehydra_defineObjectProperty (Dehydra *this, JSObject *obj, char const *name); void dehydra_init(Dehydra *this, const char *file, const char *version); int dehydra_startup (Dehydra *this); int dehydra_visitType(Dehydra *this, tree c); void dehydra_visitDecl (Dehydra *this, tree f); void dehydra_moveDefaults (Dehydra *this, JSObject *obj); JSObject* dehydra_addVar(Dehydra *this, tree v, JSObject *parentArray); void dehydra_input_end (Dehydra *this); void dehydra_print(Dehydra *this, jsval val); jsval dehydra_getToplevelFunction(Dehydra *this, char const *name); void dehydra_addAttributes (Dehydra *this, JSObject *destArray, tree attributes); int dehydra_rootObject (Dehydra *this, jsval val); void dehydra_unrootObject (Dehydra *this, int pos); jsval dehydra_getRootedObject (Dehydra *this, int pos); void dehydra_setLoc(Dehydra *this, JSObject *obj, tree t); void convert_location_t (struct Dehydra *this, struct JSObject *parent, const char *propname, location_t loc); int dehydra_includeScript(Dehydra *this, const char *filename); void dehydra_appendToPath (Dehydra *this, const char *dir); void dehydra_appendDirnameToPath (Dehydra *this, const char *filename); FILE *dehydra_searchPath (Dehydra *this, const char *filename, char **realname); bool isGPlusPlus (); void dehydra_setFilename(Dehydra *this); #endif dehydra-89ed48e70997/dehydra_ast.c0000644000000000000000000003533411757635752016631 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include "gcc_compat.h" #include "xassert.h" #include "dehydra.h" #include "dehydra_types.h" #include "util.h" static void dehydra_iterate_statementlist (Dehydra *, tree); static tree statement_walker (tree *, int *walk_, void *); static void dehydra_nextStatement(Dehydra *this, location_t loc); /* Make a Dehydra variable to represent the value of the given * AST expression node. * This function does not itself create a Dehydra variable. Instead, * it relies on the tree walk of the tree to create a Dehydra variable * by calling dehydra_addVar at some point. */ /*todo: make this avoid consecutive vars of the same thing */ JSObject* dehydra_makeVar (Dehydra *this, tree t, char const *prop, JSObject *attachToObj) { unsigned int length = dehydra_getArrayLength (this, this->destArray); this->inExpr++; cp_walk_tree_without_duplicates (&t, statement_walker, this); this->inExpr--; xassert (length < dehydra_getArrayLength (this, this->destArray)); jsval v; JS_GetElement (this->cx, this->destArray, length, &v); JSObject *obj = v == JSVAL_VOID ? NULL : JSVAL_TO_OBJECT (v); if (prop && attachToObj && obj) { dehydra_defineProperty (this, attachToObj, prop, v); JS_SetArrayLength (this->cx, this->destArray, length); } return obj; } #ifndef DEHYDRA_INPLACE_ARGS static void dehydra_fcallDoArgs (Dehydra *this, JSObject *obj, tree expr, int i, int count) { JSObject *tmp = this->destArray; this->destArray = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, ARGUMENTS, OBJECT_TO_JSVAL (this->destArray)); for (; i < count;i++) { tree e = GENERIC_TREE_OPERAND(expr, i); cp_walk_tree_without_duplicates(&e, statement_walker, this); } this->destArray = tmp; } #endif /* messes with dest-array, returns the */ static jsval dehydra_attachNestedFields(Dehydra *this, JSObject *obj, char const *name, tree t) { JSObject *tmp = this->destArray; this->destArray = JS_NewArrayObject(this->cx, 0, NULL); jsval ret = OBJECT_TO_JSVAL (this->destArray); dehydra_defineProperty(this, obj, name, ret); cp_walk_tree_without_duplicates(&t, statement_walker, this); this->destArray = tmp; return ret; } /* borrowed from cp/error.c */ static tree resolve_virtual_fun_from_obj_type_ref (tree ref) { tree obj_type = TREE_TYPE (OBJ_TYPE_REF_OBJECT (ref)); HOST_WIDE_INT index = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1); tree fun = BINFO_VIRTUALS (TYPE_BINFO (TREE_TYPE (obj_type))); while (index) { fun = TREE_CHAIN (fun); index -= (TARGET_VTABLE_USES_DESCRIPTORS ? TARGET_VTABLE_USES_DESCRIPTORS : 1); } return BV_FN (fun); } /* Does variable initialization and special case logic for stack variables being initialized by a constructor. */ void dehydra_initVar (Dehydra *this, tree lval, tree init, bool rotate) { unsigned int objPos = dehydra_getArrayLength (this, this->destArray); JSObject *obj = dehydra_makeVar (this, lval, NULL, NULL); xassert (obj); /* now add constructor */ /* note here we are assuming that last addVar as the last declaration */ /* op 0 is an anonymous temporary..i think..so use last var instead */ if (!init) return; jsval val = dehydra_attachNestedFields (this, obj, ASSIGN, init); JSObject *assignArray = JSVAL_TO_OBJECT (val); unsigned int assignArrayLength = dehydra_getArrayLength (this, assignArray); /* Ensure the world makes sense and nothing but except for a possible constructor is in assignArray */ if (assignArrayLength != 1) return; JS_GetElement (this->cx, assignArray, 0, &val); JSObject *objConstructor = JSVAL_TO_OBJECT (val); /* verify that we got a constructor */ JS_GetProperty(this->cx, objConstructor, DH_CONSTRUCTOR, &val); if (val == JSVAL_TRUE) { /* swap obj<->objConstructor if constructor */ dehydra_defineProperty (this, objConstructor, FIELD_OF, OBJECT_TO_JSVAL (obj)); if (rotate) { /* replace obj with objConstructor */ JS_DefineElement (this->cx, this->destArray, objPos, OBJECT_TO_JSVAL(objConstructor), NULL, NULL, JSPROP_ENUMERATE); /* finish up by deleting assign */ JS_DeleteProperty (this->cx, obj, ASSIGN); } } } JSObject* dehydra_call_or_aggr_init_expr (Dehydra *this, tree t) { xassert (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR); tree fn = TREE_CODE (t) == CALL_EXPR ? CALL_EXPR_FN (t) : AGGR_INIT_EXPR_FN (t); if (TREE_CODE (fn) == ADDR_EXPR) fn = TREE_OPERAND (fn, 0); int offset = 0; JSObject *obj = dehydra_makeVar (this, fn, NULL, NULL); dehydra_defineProperty (this, obj, FCALL, JSVAL_TRUE); tree args; if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE) { // need to generalize this arg_init_expr tree o = call_expr_first_arg(t); ++offset; xassert (dehydra_makeVar (this, o, FIELD_OF, obj)); // same as above args = call_expr_rest_args(t); } #ifdef DEHYDRA_INPLACE_ARGS else { args = call_expr_args(t); } dehydra_attachNestedFields(this, obj, ARGUMENTS, args); #else dehydra_fcallDoArgs (this, obj, t, call_expr_first_param_index + offset, TREE_OPERAND_LENGTH(t)); #endif return obj; } static const int enable_ast_debug = 0; static int statement_walker_depth = 0; static tree statement_walker (tree *tp, int *walk_subtrees, void *data) { Dehydra *this = data; enum tree_code code = TREE_CODE(*tp); if (enable_ast_debug) { location_t prior_loc = UNKNOWN_LOCATION; statement_walker_depth++; char *spaces = xmalloc(statement_walker_depth+1); int d; for (d = 0; d < statement_walker_depth;d++) { spaces[d] = '-'; } spaces[statement_walker_depth] = 0; location_t loc = location_of(*tp); if (loc_is_unknown(loc)) loc = prior_loc; else prior_loc = loc; warning(0, "%H%sast: %s %s",&loc,spaces,tree_code_name[TREE_CODE(*tp)], DECL_P(*tp) ? expr_as_string(*tp, 0) : ""); free(spaces); } switch (code) { case STATEMENT_LIST: *walk_subtrees = 0; dehydra_iterate_statementlist(this, *tp); break; case DECL_EXPR: { tree e = *tp; tree decl = GENERIC_TREE_OPERAND (e, 0); if (TREE_CODE (decl) != USING_DECL) { tree init = DECL_INITIAL (decl); dehydra_initVar (this, decl, init, false); /*JSObject *obj = dehydra_addVar(this, decl, NULL); dehydra_defineProperty(this, obj, DECL, JSVAL_TRUE); if (init) { dehydra_attachNestedFields(this, obj, ASSIGN, init); }*/ } *walk_subtrees = 0; break; } case INIT_EXPR: { tree lval = GENERIC_TREE_OPERAND (*tp, 0); tree init = GENERIC_TREE_OPERAND(*tp, 1); dehydra_initVar (this, lval, init, true); } *walk_subtrees = 0; break; case TARGET_EXPR: { /* This is a weird initializer tree node. It seems to be used to * decorate regular initializers to add cleanup code, etc. We'll * just skip the wrapping. */ cp_walk_tree_without_duplicates(&TARGET_EXPR_INITIAL(*tp), statement_walker, this); *walk_subtrees = 0; break; } case AGGR_INIT_EXPR: { /* C++ constructor invocation. */ dehydra_call_or_aggr_init_expr (this, *tp); *walk_subtrees = 0; break; } case CONSTRUCTOR: { /* In C, this is used for struct/array initializers. It appears * that in C++ it is also used for default constructor invocations. */ (void) dehydra_addVar(this, *tp, NULL); /* This tree type has an argument list, but the arguments should * all be default zero values of no interest. */ *walk_subtrees = 0; break; } /* these wrappers all contain important stuff as first arg */ case OBJ_TYPE_REF: /* resovle virtual calls */ { tree fn = resolve_virtual_fun_from_obj_type_ref (*tp); JSObject *obj = dehydra_addVar (this, fn, NULL); xassert (dehydra_makeVar (this, OBJ_TYPE_REF_OBJECT (*tp), FIELD_OF, obj)); } *walk_subtrees = 0; break; case CLEANUP_POINT_EXPR: // do not walk the cleanup stuff cp_walk_tree_without_duplicates (&GENERIC_TREE_OPERAND (*tp, 0), statement_walker, this); *walk_subtrees = 0; break; case CALL_EXPR: { dehydra_call_or_aggr_init_expr (this, *tp); *walk_subtrees = 0; break; } case MODIFY_EXPR: case PREDECREMENT_EXPR: case PREINCREMENT_EXPR: case POSTDECREMENT_EXPR: case POSTINCREMENT_EXPR: { JSObject *obj = dehydra_makeVar (this, GENERIC_TREE_OPERAND (*tp, 0), NULL, NULL); if (obj) { dehydra_attachNestedFields (this, obj, ASSIGN, GENERIC_TREE_OPERAND (*tp, 1)); } *walk_subtrees = 0; break; } case COMPONENT_REF: { JSObject *obj = dehydra_addVar (this, GENERIC_TREE_OPERAND(*tp, 1), NULL); xassert (dehydra_makeVar (this, GENERIC_TREE_OPERAND (*tp, 0), FIELD_OF, obj)); } *walk_subtrees = 0; break; case REAL_CST: case INTEGER_CST: #ifdef FIXED_CST_CHECK case FIXED_CST: #endif case COMPLEX_CST: case VECTOR_CST: case STRING_CST: case PTRMEM_CST: { tree type = TREE_TYPE (*tp); JSObject *obj = dehydra_addVar (this, NULL_TREE, NULL); /* Bug 429362: GCC pretty-printer is broken for unsigned ints. */ dehydra_defineStringProperty (this, obj, VALUE, code == INTEGER_CST ? dehydra_intCstToString(*tp) : expr_as_string(*tp, 0)); if (type) { dehydra_defineProperty (this, obj, TYPE, dehydra_convert_type(this, type)); } } break; case RETURN_EXPR: { tree expr = GENERIC_TREE_OPERAND (*tp, 0); if (expr && TREE_CODE (expr) != RESULT_DECL) { if (TREE_CODE (expr) == INIT_EXPR) { expr = GENERIC_TREE_OPERAND (expr, 1); } JSObject *obj = dehydra_makeVar (this, expr, NULL, NULL); xassert (obj); dehydra_defineProperty (this, obj, RETURN, JSVAL_TRUE); } *walk_subtrees = 0; break; } case EXPR_STMT: if (!this->inExpr) { location_t loc = location_of (*tp); if (!loc_is_unknown (loc)) dehydra_nextStatement (this, loc); } break; case TRY_CATCH_EXPR: #ifdef POINTER_PLUS_EXPR_CHECK case POINTER_PLUS_EXPR: #endif case ADDR_EXPR: case INDIRECT_REF: case LABEL_DECL: // above expressions have a single operand break; default: { tree maybe_decl = *tp; if (code == PTRMEM_CST) { maybe_decl = PTRMEM_CST_MEMBER (maybe_decl); code = TREE_CODE (maybe_decl); } if (code != NAMESPACE_DECL && DECL_P(maybe_decl)) { dehydra_addVar (this, maybe_decl, NULL); } } break; } if (enable_ast_debug) { statement_walker_depth--; } /* fprintf(stderr, "%s:", loc_as_string(this->loc)); fprintf(stderr, "walking tree element: %s. %s\n", tree_code_name[TREE_CODE(*tp)], expr_as_string(*tp, 0));*/ return NULL_TREE; } /* Creates next array to dump dehydra objects onto */ static void dehydra_nextStatement(Dehydra *this, location_t loc) { unsigned int length = dehydra_getArrayLength (this, this->statementHierarchyArray); xassert (!this->inExpr); this->loc = loc; this->destArray = NULL; JSObject *obj = NULL; /* Check that the last statement array was used, otherwise reuse it */ if (length) { jsval val; JS_GetElement (this->cx, this->statementHierarchyArray, length - 1, &val); obj = JSVAL_TO_OBJECT (val); JS_GetProperty (this->cx, obj, STATEMENTS, &val); this->destArray = JSVAL_TO_OBJECT (val); int destLength = dehydra_getArrayLength (this, this->destArray); /* last element is already setup & empty, we are done */ if (destLength != 0) { this->destArray = NULL; } } if (!this->destArray) { obj = JS_NewObject (this->cx, NULL, 0, 0); JS_DefineElement (this->cx, this->statementHierarchyArray, length, OBJECT_TO_JSVAL(obj), NULL, NULL, JSPROP_ENUMERATE); this->destArray = JS_NewArrayObject(this->cx, 0, NULL); dehydra_defineProperty (this, obj, STATEMENTS, OBJECT_TO_JSVAL (this->destArray)); } convert_location_t(this, obj, LOC, this->loc); } static void dehydra_iterate_statementlist (Dehydra *this, tree statement_list) { tree_stmt_iterator i = tsi_start (STATEMENT_LIST_CHECK( statement_list)); for (; !tsi_end_p (i); tsi_next (&i)) { tree s = *tsi_stmt_ptr (i); /* weird statements within expression shouldn't trigger dehydra_nextStatement */ if (! this->inExpr) dehydra_nextStatement (this, location_of (s)); cp_walk_tree_without_duplicates (&s, statement_walker, this); } } void dehydra_cp_pre_genericize(Dehydra *this, tree fndecl, bool callJS) { this->statementHierarchyArray = JS_NewArrayObject (this->cx, 0, NULL); int key = dehydra_rootObject (this, OBJECT_TO_JSVAL (this->statementHierarchyArray)); *pointer_map_insert (this->fndeclMap, fndecl) = (void*)(intptr_t) key; dehydra_nextStatement (this, location_of (fndecl)); tree body_chain = DECL_SAVED_TREE (fndecl); if (body_chain && TREE_CODE (body_chain) == BIND_EXPR) { body_chain = BIND_EXPR_BODY (body_chain); } cp_walk_tree_without_duplicates (&body_chain, statement_walker, this); this->statementHierarchyArray = NULL; if (callJS) { dehydra_visitDecl (this, fndecl); } } dehydra-89ed48e70997/dehydra_ast.h0000644000000000000000000000217111757635752016627 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DEHYDRA_AST_H #define DEHYDRA_AST_H void dehydra_cp_pre_genericize(Dehydra *this, tree fndecl, bool callJS); JSObject* dehydra_makeVar (Dehydra *this, tree t, char const *prop, JSObject *attachToObj); #endif dehydra-89ed48e70997/dehydra_builtins.c0000644000000000000000000004161111757635752017666 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include "cp-tree-jsapi-workaround.h" #include #include "util.h" #include "dehydra.h" #include "dehydra_builtins.h" #include "xassert.h" #ifdef TREEHYDRA_PLUGIN extern int treehydra_debug; int set_after_gcc_pass(const char *pass); #endif JSBool require_version(JSContext *cx, jsval val) { JSString *version_str = JS_ValueToString(cx, val); if (!version_str) return JS_FALSE; char *version_cstr = JS_EncodeString(cx, version_str); xassert(version_cstr); JSVersion version = JS_StringToVersion(version_cstr); JSBool retval; if (version == JSVERSION_UNKNOWN) { JS_ReportError(cx, "Invalid version '%s'", version_cstr); retval = JS_FALSE; } else { JS_SetVersion(cx, version); retval = JS_TRUE; } JS_free(cx, version_cstr); return retval; } JSBool require_option(JSContext *cx, jsval val, uint32 option) { JSBool flag; if (!JS_ValueToBoolean(cx, val, &flag)) return JS_FALSE; if (flag) { JS_SetOptions(cx, JS_GetOptions(cx) | option); } else { JS_SetOptions(cx, JS_GetOptions(cx) & ~option); } return JS_TRUE; } #ifdef TREEHYDRA_PLUGIN JSBool require_pass(JSContext *cx, jsval val) { JSString *str = JS_ValueToString(cx, val); if (!str) return JS_FALSE; JS_AddStringRoot(cx, &str); char *cstr = JS_EncodeString(cx, str); xassert(cstr); JSBool retval; if (set_after_gcc_pass(cstr)) { JS_ReportError(cx, "Cannot set gcc_pass_after after initialization is finished"); retval = JS_FALSE; } else { retval = JS_TRUE; } JS_free(cx, cstr); JS_RemoveStringRoot(cx, &str); return retval; } #endif JSBool dispatch_require(JSContext *cx, const char *prop_name, jsval prop_val) { if (strcmp(prop_name, "version") == 0) { return require_version(cx, prop_val); } else if (strcmp(prop_name, "strict") == 0) { return require_option(cx, prop_val, JSOPTION_STRICT); } else if (strcmp(prop_name, "werror") == 0) { return require_option(cx, prop_val, JSOPTION_WERROR); } else if (strcmp(prop_name, "gczeal") == 0) { #ifdef JS_GC_ZEAL uintN zeal; if (!JS_ValueToECMAUint32(cx, prop_val, &zeal)) return JS_FALSE; JS_SetGCZeal(cx, zeal); #else #ifdef DEBUG JS_ReportWarning(cx, "gczeal not available: xhydra built with a SpiderMonkey version" " lacking JS_SetGCZeal"); #else JS_ReportWarning(cx, "gczeal not available: xhydra built without -DDEBUG"); #endif //DEBUG #endif //JS_GC_ZEAL return JS_TRUE; #ifdef TREEHYDRA_PLUGIN } else if (strcmp(prop_name, "after_gcc_pass") == 0) { return require_pass(cx, prop_val); } else if (strcmp(prop_name, "treehydra_debug") == 0) { treehydra_debug = 1; return JS_TRUE; #endif } else { JS_ReportWarning(cx, "Unrecognized require keyword '%s'", prop_name); return JS_TRUE; } } /* Helper to return the current version as a JS string. */ jsval get_version(JSContext *cx) { const char *version_cstr = JS_VersionToString(JS_GetVersion(cx)); if (version_cstr == NULL) { return JSVAL_VOID; } JSString *version_str = JS_NewStringCopyZ(cx, version_cstr); return STRING_TO_JSVAL(version_str); } JSBool Require(JSContext *cx, uintN argc, jsval *vp) { JSObject *args; if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o", &args)) return JS_FALSE; JSIdArray *prop_ids = JS_Enumerate(cx, args); if (!prop_ids) return JS_FALSE; /* Apply the options. */ JSBool retval = JS_TRUE; int i; for (i = 0; i < prop_ids->length; ++i) { xassert(JS_EnterLocalRootScope(cx)); jsval prop; JSBool rv = JS_IdToValue(cx, prop_ids->vector[i], &prop); xassert(rv); JSString *prop_str = JSVAL_TO_STRING(prop); char *prop_name = JS_EncodeString(cx, prop_str); xassert(prop_name); jsval prop_val; rv = JS_GetProperty(cx, args, prop_name, &prop_val); xassert(rv); rv = dispatch_require(cx, prop_name, prop_val); if (rv == JS_FALSE) retval = JS_FALSE; JS_free(cx, prop_name); JS_LeaveLocalRootScope(cx); } JS_DestroyIdArray(cx, prop_ids); if (!retval) return retval; /* Report the now-current options. */ JSObject *rvalo = JS_NewObject(cx, NULL, NULL, NULL); if (!rvalo) return JS_FALSE; JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(rvalo)); JS_DefineProperty( cx, rvalo, "version", get_version(cx), NULL, NULL, JSPROP_ENUMERATE); uint32 options = JS_GetOptions(cx); JS_DefineProperty( cx, rvalo, "strict", (options | JSOPTION_STRICT) ? JSVAL_TRUE : JSVAL_FALSE, NULL, NULL, JSPROP_ENUMERATE); JS_DefineProperty( cx, rvalo, "werror", (options | JSOPTION_WERROR) ? JSVAL_TRUE : JSVAL_FALSE, NULL, NULL, JSPROP_ENUMERATE); return JS_TRUE; } JSBool Print(JSContext *cx, uintN argc, jsval *vp) { uintN i; /* don't touch stdout if it's being piped to assembler */ FILE *out = (!strcmp(asm_file_name, "-") && ! flag_syntax_only) ? stderr : stdout; jsval *argv = JS_ARGV(cx, vp); for (i = 0; i < argc; i++) { JSString *str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; char *bytes = JS_EncodeString(cx, str); xassert(bytes); fprintf(out, "%s", bytes); JS_free(cx, bytes); } fprintf(out, "\n"); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } typedef void (*diagnostic_func)(const char *, ...); JSBool Diagnostic(JSContext *cx, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); JSBool is_error; JSObject *loc_obj = NULL; if (!JS_ConvertArguments(cx, argc, argv, "b*/o", &is_error, &loc_obj)) return JS_FALSE; if (!JSVAL_IS_STRING(argv[1])) return JS_FALSE; char *msg = JS_EncodeString(cx, JSVAL_TO_STRING(argv[1])); xassert(msg); if (loc_obj) { location_t loc; #if defined(__APPLE__) jsval jsfile, jsline; if (JS_GetProperty(cx, loc_obj, "file", &jsfile) && JS_GetProperty(cx, loc_obj, "line", &jsline)) { loc.file = JS_GetStringBytes(JSVAL_TO_STRING(jsfile)); loc.line = JSVAL_TO_INT(jsline); #else jsval jsloc; if (JS_GetProperty(cx, loc_obj, "_source_location", &jsloc)) { loc = JSVAL_TO_INT(jsloc); #endif // gcc 4.3 #ifdef GIMPLE_TUPLE_P if (is_error) error ("%H%s", &loc, msg); else warning (0, "%H%s", &loc, msg); // gcc 4.5 #else if (is_error) error_at (loc, "%s", msg); else warning_at (loc, 0, "%s", msg); #endif } } else if (is_error) { error ("%s", msg); } else { warning (0, "%s", msg); } JS_free(cx, msg); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } /* Report an error. * If we are currently inside JS, we'll report an error to JS. But * otherwise, we'll report it to the user and then exit. */ void reportError(JSContext *cx, const char *file, int line, const char *fmt, ...) { char msg[1024]; const int size = sizeof(msg) / sizeof(msg[0]); va_list ap; va_start(ap, fmt); int nw = vsnprintf(msg, size, fmt, ap); va_end(ap); if (nw >= size) msg[size-1] = '\0'; if (JS_IsRunning(cx)) { JS_ReportError(cx, "%s (from %s:%d)", msg, file, line); } else { fflush(stdout); fprintf(stderr, "%s:%d: Error: %s\n", file, line, msg); exit(1); } } void ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { int error = JSREPORT_IS_EXCEPTION(report->flags); jsval exn; fflush(stdout); fprintf(stderr, "%s:%d: ", (report->filename ? report->filename : "NULL"), report->lineno); if (JSREPORT_IS_WARNING(report->flags)) fprintf(stderr, "JS Warning"); if (JSREPORT_IS_STRICT(report->flags)) fprintf(stderr, "JS STRICT"); if (error) fprintf(stderr, "JS Exception"); fprintf(stderr, ": %s\n", message); if (report->linebuf) { fprintf(stderr, "%s\n", report->linebuf); } if (error && JS_GetPendingException(cx, &exn) && JS_TypeOfValue (cx, exn) == JSTYPE_OBJECT) { jsval stack; /* reformat the spidermonkey stack */ JS_GetProperty(cx, JSVAL_TO_OBJECT (exn), "stack", &stack); if (JS_TypeOfValue (cx, stack) == JSTYPE_STRING) { char *bytes = JS_EncodeString(cx, JSVAL_TO_STRING (stack)); xassert(bytes); char *str = bytes; int counter = 0; do { char *eol = strchr (str, '\n'); if (eol) *eol = 0; char *at = strrchr (str, '@'); if (!at) break; *at = 0; if (!*str) break; fprintf (stderr, "%s:\t#%d: %s\n", at+1, counter++, str); *at = '@'; if (eol) { *eol = '\n'; str = eol + 1; } else { break; } } while (*str); JS_free(cx, bytes); } } fflush(stderr); if (!JSREPORT_IS_WARNING(report->flags)) exit(1); } JSBool ReadFile(JSContext *cx, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE; char *filename = JS_EncodeString(cx, JSVAL_TO_STRING(argv[0])); xassert(filename); long size = 0; char *buf = readFile (filename, &size); JSBool rv = JS_FALSE; if(!buf) { REPORT_ERROR_2(cx, "read_file: error opening file '%s': %s", filename, xstrerror(errno)); } else { JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(JS_NewStringCopyN(cx, buf, size))); rv = JS_TRUE; } JS_free(cx, filename); return rv; } JSBool WriteFile(JSContext *cx, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); JSString *str; if (!JS_ConvertArguments(cx, argc, argv, "*S", &str)) return JS_FALSE; if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE; char *filename = JS_EncodeString(cx, JSVAL_TO_STRING(argv[0])); xassert(filename); JSBool rv = JS_FALSE; FILE *f = fopen (filename, "w"); if (!f) { REPORT_ERROR_2(cx, "write_file: error opening file '%s': %s", filename, xstrerror(errno)); } else { char *bytes = JS_EncodeString(cx, str); xassert(bytes); fwrite (bytes, 1, JS_GetStringLength(str), f); fclose (f); JS_free(cx, bytes); JS_SET_RVAL(cx, vp, JSVAL_VOID); rv = JS_TRUE; } JS_free(cx, filename); return rv; } char *readEntireFile(FILE *f, long *size) { xassert(f); if (fseek(f, 0, SEEK_END)) return NULL; *size = ftell(f); if (fseek(f, 0, SEEK_SET)) return NULL; char *buf = xmalloc(*size + 1); xassert(*size == fread(buf, 1, *size, f)); buf[*size] = 0; fclose(f); return buf; } /* Read the entire contents of a file. * path path of the file to read * size (out) number of bytes of file data read * return null-terminated file contents, or NULL on error. Caller * should free when done. */ char *readFile(const char *path, long *size) { FILE *f = fopen(path, "r"); if (!f) return NULL; return readEntireFile(f, size); } /* Find a file, searching another dir if necessary. If the file is * found, return a file handle open for reading and store the malloc'd * name where the file was found in realname. Otherwise, return * NULL. */ FILE *findFile(const char *filename, const char *dir, char **realname) { FILE *f = fopen(filename, "r"); if (f) { *realname = xstrdup(filename); return f; } if (dir && dir[0] && filename[0] && filename[0] != '/') { char *buf = xmalloc(strlen(dir) + strlen(filename) + 2); /* Doing a little extra work here to get rid of unneeded '/'. */ const char *sep = dir[strlen(dir)-1] == '/' ? "" : "/"; sprintf(buf, "%s%s%s", dir, sep, filename); f = fopen(buf, "r"); if (f) { *realname = buf; return f; } else { free(buf); } } return NULL; } /* Load and run the named script. The last argument is the object to use as "this" when evaluating the script, which is effectively a namespace for the script. */ static JSBool dehydra_loadScript (Dehydra *this, const char *filename, JSObject *namespace) { /* Read the file. There's a JS function for reading scripts, but Dehydra wants to search for the file in different dirs. */ long size = 0; char *realname; FILE *f = dehydra_searchPath(this, filename, &realname); //FILE *f = findFile(filename, 0, &realname); // TODO this->dir, &realname); if (!f) { REPORT_ERROR_1(this->cx, "Cannot find include file '%s'", filename); return JS_FALSE; } char *content = readEntireFile(f, &size); if (!content) { REPORT_ERROR_1(this->cx, "Cannot read include file '%s'", realname); free(realname); return JS_FALSE; } JSObject *sobj = JS_CompileScript(this->cx, namespace, content, size, realname, 1); free(realname); if (sobj == NULL) { xassert(JS_IsExceptionPending(this->cx)); return JS_FALSE; } JS_AddNamedObjectRoot(this->cx, &sobj, filename); jsval rval; JSBool rv = JS_ExecuteScript(this->cx, namespace, sobj, &rval); JS_RemoveObjectRoot(this->cx, &sobj); if (!rv) { xassert(JS_IsExceptionPending(this->cx)); return JS_FALSE; } return JS_TRUE; } /* should use this function to load all objects to avoid possibity of objects including themselves */ JSBool Include(JSContext *cx, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE; char *filename = JS_EncodeString(cx, JSVAL_TO_STRING(argv[0])); xassert(filename); Dehydra *this = JS_GetContextPrivate(cx); JSObject *namespace = this->globalObj; if (!JS_ConvertArguments(cx, argc, argv, "*/o", &namespace)) return JS_FALSE; JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(namespace)); JSObject *includedArray = NULL; jsval val; JS_GetProperty(cx, namespace, "_includedArray", &val); if (!JSVAL_IS_OBJECT (val)) { includedArray = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, namespace, "_includedArray", OBJECT_TO_JSVAL (includedArray)); } else { includedArray = JSVAL_TO_OBJECT (val); xassert (JS_CallFunctionName (this->cx, includedArray, "lastIndexOf", 1, argv, &val)); /* Return if file was already included in this namespace. */ if (JSVAL_TO_INT (val) != -1) { JS_free(cx, filename); return JS_TRUE; } } JS_CallFunctionName (this->cx, includedArray, "push", 1, argv, &JS_RVAL(cx, vp)); JSBool rv = dehydra_loadScript (this, filename, namespace); JS_free(cx, filename); return rv; } /* author: tglek Return the primitive if it's a primitive, otherwise compute a seq # The ES4 spec says that it shouldn't be a pointer(with good reason). A counter is morally wrong because in theory it could loop around and bite me, but I lack in moral values and don't enjoy abusing pointers any further */ JSBool Hashcode(JSContext *cx, uintN argc, jsval *vp) { if (!argc) return JS_FALSE; jsval o = *JS_ARGV(cx, vp); if (!JSVAL_IS_OBJECT (o)) { JS_SET_RVAL(cx, vp, o); return JS_TRUE; } JSObject *obj = JSVAL_TO_OBJECT (o); JSBool has_prop; /* Need to check for property first to keep treehydra from getting angry */ #if JS_VERSION < 180 #define JS_AlreadyHasOwnProperty JS_HasProperty #endif if (JS_AlreadyHasOwnProperty(cx, obj, "_hashcode", &has_prop) && has_prop) { jsval rval; JS_GetProperty(cx, obj, "_hashcode", &rval); JS_SET_RVAL(cx, vp, rval); } else { static int counter = 0; char str[256]; jsval val; snprintf (str, sizeof (str), "%x", ++counter); val = STRING_TO_JSVAL (JS_NewStringCopyZ (cx, str)); JS_DefineProperty (cx, obj, "_hashcode", val, NULL, NULL, JSPROP_PERMANENT | JSPROP_READONLY); JS_SET_RVAL(cx, vp, val); } return JS_TRUE; } JSBool ResolvePath(JSContext *cx, uintN argc, jsval *vp) { jsval *argv = JS_ARGV(cx, vp); if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE; char *path = JS_EncodeString(cx, JSVAL_TO_STRING(argv[0])); xassert(path); char buf[PATH_MAX]; char *buf_rv = realpath(path, buf); JSBool rv = JS_FALSE; if (!buf_rv) { REPORT_ERROR_2(cx, "resolve_path: error resolving path '%s': %s", path, xstrerror(errno)); } else { JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf))); rv = JS_TRUE; } JS_free(cx, path); return rv; } dehydra-89ed48e70997/dehydra_builtins.h0000644000000000000000000000405711757635752017676 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DEHYDRA_BUILTINS_H #define DEHYDRA_BUILTINS_H /* Support older SpiderMonkey versions where JSNative != JSFastNative */ #ifdef JSFUN_FAST_NATIVE #define DH_JS_FN JS_FN #else #define DH_JS_FN(a, b, c, d, e) JS_FN(a, b, c, d) #define JSFUN_FAST_NATIVE 0 #endif /* JS Natives */ #define DH_JSNATIVE(fname) JSBool fname(JSContext *cx, uintN argc, jsval *vp); DH_JSNATIVE(Require); DH_JSNATIVE(Include); DH_JSNATIVE(Diagnostic); DH_JSNATIVE(Print); DH_JSNATIVE(WriteFile); DH_JSNATIVE(ReadFile); DH_JSNATIVE(Hashcode); DH_JSNATIVE(ResolvePath); void ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report); /* Related C functions */ char *readFile(const char *path, long *size); FILE *findFile(const char *filename, const char *dir, char **realname); char *readEntireFile(FILE *f, long *size); void reportError(JSContext *cx, const char *file, int line, const char *fmt, ...); #define REPORT_ERROR_0(cx, fmt) reportError(cx, __FILE__, __LINE__, fmt); #define REPORT_ERROR_1(cx, fmt, arg1) reportError(cx, __FILE__, __LINE__, fmt, arg1); #define REPORT_ERROR_2(cx, fmt, arg1, arg2) reportError(cx, __FILE__, __LINE__, fmt, arg1, arg2); #endif dehydra-89ed48e70997/dehydra_plugin.c0000644000000000000000000003516311757635752017340 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dehydra-config.h" /*js*/ #include // cgraph.h conflicts with nspr #undef TEST_BIT #include "gcc_cp_headers.h" #include "xassert.h" #include "dehydra.h" #include "dehydra_ast.h" #include "dehydra_types.h" #include "util.h" #include "cgraph.h" #include "tree-pass.h" #ifdef TREEHYDRA_PLUGIN #include "treehydra.h" #include "tree-pass.h" /* True if initialization has been completed. */ static int init_finished = 0; /* Plugin pass position. This is kept here so that JS can set it. */ static char *after_gcc_pass = 0; #endif #include "gcc-plugin.h" #include "plugin.h" /* Queue up tree object for latest processing (ie because gcc will fill in more info or loose track of them) */ static VEC(tree,heap) *tree_queue_vec = NULL; static int processed = 0; static Dehydra dehydra = {0}; static void process(tree); static void process_type(tree t); static void dehydra_ehqueue_tree (tree t); // do a DFS of a TREE_CHAIN void dfs_process_chain(tree t) { if (!t) return; dfs_process_chain(TREE_CHAIN(t)); process(t); } static void process_template_decl (tree td) { tree inst; // process all of the template instantiations too for (inst = DECL_TEMPLATE_INSTANTIATIONS (td); inst; inst = TREE_CHAIN (inst)) { tree record_type = TREE_VALUE (inst); if (TREE_CODE (record_type) == RECORD_TYPE || TREE_CODE (record_type) == UNION_TYPE || TREE_CODE (record_type) == QUAL_UNION_TYPE) process_type (record_type); } } static void process_namespace_decl (tree ns) { if (DECL_NAMESPACE_ALIAS (ns)) return; struct cp_binding_level *level = NAMESPACE_LEVEL (ns); dfs_process_chain(level->names); for (ns = level->namespaces; ns; ns = TREE_CHAIN(ns)) { process_namespace_decl(ns); } } static void process_decl (tree f) { if (TREE_CODE(f) == TYPE_DECL && DECL_ORIGINAL_TYPE(f)) { f = TREE_TYPE(f); dehydra_visitType (&dehydra, f); #ifdef TREEHYDRA_PLUGIN treehydra_call_js (&dehydra, "process_tree_type", f); #endif } else { dehydra_visitDecl (&dehydra, f); #ifdef TREEHYDRA_PLUGIN treehydra_call_js (&dehydra, "process_tree_decl", f); #endif } JS_MaybeGC(dehydra.cx); } static void process_record_or_union_type (tree c) { tree field, func; if (!COMPLETE_TYPE_P (c)) return; // bug 418170: don't process template specializations if (isGPlusPlus() && CLASSTYPE_USE_TEMPLATE(c) == 2) { // bug 449428: want all template decls process (TYPE_NAME (c)); return; } //fprintf(stderr, "class %s\n", type_as_string(c, 0)); /* iterate over base classes to ensure they are visited first */ tree binfo = TYPE_BINFO (c); int n_baselinks = binfo ? BINFO_N_BASE_BINFOS (binfo) : 0; int i; for (i = 0; i < n_baselinks; i++) { tree base_binfo = BINFO_BASE_BINFO (binfo, i); process_type (BINFO_TYPE (base_binfo)); } /* Output all the method declarations in the class. */ for (func = TYPE_METHODS (c) ; func ; func = TREE_CHAIN (func)) { if (DECL_ARTIFICIAL(func)) continue; /* Don't output the cloned functions. */ if (DECL_CLONED_FUNCTION_P (func)) continue; process(func); } for (field = TYPE_FIELDS (c) ; field ; field = TREE_CHAIN (field)) { if (DECL_ARTIFICIAL(field) && !DECL_IMPLICIT_TYPEDEF_P(field)) continue; // ignore typedef of self field // my theory is that the guard above takes care of this one too if (TREE_CODE (field) == TYPE_DECL && TREE_TYPE (field) == c) continue; process(field); //fprintf(stderr, "%s: member %s\n", loc(c), tree_code_name[TREE_CODE(field)]); } xassert (COMPLETE_TYPE_P (c)); dehydra_visitType (&dehydra, c); //fprintf(stderr, "/class //%s\n", type_as_string(c, 0)); } static void process_enumeral_type (tree enumeral) { dehydra_visitType (&dehydra, enumeral); } // guard against duplicate visits static struct pointer_set_t *pset = NULL; static struct pointer_set_t *type_pset = NULL; static void process_type(tree t) { if (pointer_set_insert (type_pset, t)) { return; } // dmandelin@mozilla.com -- bug 420299 // We need to process the original type first, because it will be the target // of the typedef field. This is the natural extension of the DFS strategy. tree type_decl = TYPE_NAME (t); if (type_decl && TREE_CODE (type_decl) == TYPE_DECL) { tree original_type = DECL_ORIGINAL_TYPE (type_decl); if (original_type) { process_type(original_type); } } switch (TREE_CODE(t)) { case RECORD_TYPE: case UNION_TYPE: process_record_or_union_type (t); break; case ENUMERAL_TYPE: process_enumeral_type (t); break; default: /* fprintf(stderr, "Unhandled type:%s\n", tree_code_name[TREE_CODE(t)]);*/ break; } JS_MaybeGC(dehydra.cx); } static void process (tree t) { xassert (DECL_P (t)); if (pointer_set_insert (pset, t)) { return; } if (TREE_CODE (t) != NAMESPACE_DECL && DECL_IS_BUILTIN (t)) { return; } // nothing interesting in using decls if (TREE_CODE(t) == USING_DECL) return; tree tree_type = TREE_TYPE (t); bool is_template_typedef = tree_type && MAYBE_CLASS_TYPE_P (tree_type) && TYPE_TEMPLATE_INFO (tree_type); bool is_artifical = (TREE_CODE (t) == TYPE_DECL && DECL_IMPLICIT_TYPEDEF_P (t)) || DECL_ARTIFICIAL (t) || DECL_IS_BUILTIN (t); // templates actually have useful info in the typedefs if (is_template_typedef || !is_artifical) process_decl (t); switch (TREE_CODE (t)) { case NAMESPACE_DECL: return process_namespace_decl (t); case TEMPLATE_DECL: return process_template_decl (t); case FIELD_DECL: case CONST_DECL: case TYPE_DECL: case FUNCTION_DECL: case VAR_DECL: return process_type (tree_type); default: /*error ( "Dehydra unknown tree element: %s", tree_code_name[TREE_CODE(t)]);*/ xassert(!DECL_P (t)); break; } } #define STRINGIFY(x) #x int gcc_plugin_init(const char *file, const struct plugin_argument* argv, int argc, char **pass, const char *version_string) { char *script = NULL; pset = pointer_set_create (); type_pset = pointer_set_create (); tree_queue_vec = VEC_alloc(tree, heap, 10); dehydra_init (&dehydra, file, version_string); int ret = dehydra_startup (&dehydra); if (ret) return ret; #ifdef TREEHYDRA_PLUGIN ret = treehydra_startup (&dehydra); if (ret) return ret; #endif JSObject *options = dehydra_defineObjectProperty (&dehydra, dehydra.globalObj, "options"); int i; for (i = 0; i < argc; ++i) { if (!strcmp(argv[i].key, "script")) script = argv[i].value; else dehydra_defineStringProperty (&dehydra, options, argv[i].key, argv[i].value); } if (!script) { error ("Use " STRINGIFY(PLUGIN_ARG) "= to specify the dehydra script to run"); return 1; } dehydra_appendDirnameToPath (&dehydra, script); ret = dehydra_includeScript (&dehydra, script); free(script); #ifdef TREEHYDRA_PLUGIN /* This has to come after including the user's script, because once * init_finished is set, the user can't set the gcc pass. */ init_finished = 1; if (after_gcc_pass) *pass = after_gcc_pass; #endif return ret; } #ifdef TREEHYDRA_PLUGIN /* API function to set the plugin pass position. This should be called * only before/during plugin initialization. * Return nonzero on failure. */ int set_after_gcc_pass(const char *pass) { if (init_finished) return 1; if (after_gcc_pass) free(after_gcc_pass); after_gcc_pass = xstrdup(pass); return 0; } void gcc_plugin_pass (void) { // TODO This is rather painful, but we really don't have any other // place to put it. if (after_gcc_pass) { free(after_gcc_pass); after_gcc_pass = 0; } treehydra_call_js (&dehydra, "process_tree", current_function_decl); } #endif /* template instations happen late and various code gets nuked so we can't hook into them the generic way. Thus this hack to make dehydra_cp_pre_genericize call dehydra_visitDecl directly */ static bool postGlobalNamespace = 0; static void gcc_plugin_post_parse(void*_, void*_2) { if (processed || errorcount) return; processed = 1; int i; tree t; /* first visit recorded structs */ for (i = 0; tree_queue_vec && VEC_iterate (tree, tree_queue_vec, i, t); ++i) { // Bug 575434: some structs get mangled with 4.5 CC1 // see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44968 // TODO: find out what's going on if (!TYPE_P (t)) continue; process_type (t); #ifdef TREEHYDRA_PLUGIN treehydra_call_js (&dehydra, "process_tree_type", t); #endif } VEC_free (tree, heap, tree_queue_vec); tree_queue_vec = NULL; if (global_namespace) process(global_namespace); // Bug 532319 - Fix processing of instantiated template functions // TODO: use cgraph_nodes + varpool to get rid of global_namespace and end up with working C support struct cgraph_node *n; for (n = cgraph_nodes; n; n = n->next) { process(n->decl); } postGlobalNamespace = 1; } static void gcc_plugin_cp_pre_genericize(tree fndecl, void *_) { if (errorcount || DECL_CLONED_FUNCTION_P (fndecl) || DECL_ARTIFICIAL(fndecl)) return; dehydra_cp_pre_genericize(&dehydra, fndecl, postGlobalNamespace); #ifdef TREEHYDRA_PLUGIN treehydra_call_js (&dehydra, "process_cp_pre_genericize", fndecl); #endif } static void gcc_plugin_finish_struct (tree t, void *_) { // gcc trunk gives us error_mark for some reason if (errorcount || TREE_CODE(t) != RECORD_TYPE) return; dehydra_finishStruct (&dehydra, t); /* It's lame but types are still instantiated after post_parse when some of the stuff saved by dehydra_cp_pre_genericize has been freed by GCC */ if (postGlobalNamespace) { process_type (t); #ifdef TREEHYDRA_PLUGIN treehydra_call_js (&dehydra, "process_tree_type", t); #endif return; } /* Appending stuff to the queue instead of processing immediately is because gcc is overly lazy and does some things (like setting anonymous struct names) sometime after completing the type */ dehydra_ehqueue_tree(t); } static void dehydra_ehqueue_tree (tree t) { VEC_safe_push(tree, heap, tree_queue_vec, t); } #ifdef CFG_FINISH_DECL static void gcc_plugin_finish_decl(void *event_data, void *_) { tree decl = (tree) event_data; if (TREE_CODE(decl) != VAR_DECL || DECL_CONTEXT(decl)) return; #ifdef TREEHYDRA_PLUGIN treehydra_call_js (&dehydra, "process_tree_decl", decl); #endif } #endif static void gcc_plugin_finish (void *_, void *_2) { pointer_set_destroy (pset); pset = NULL; pointer_set_destroy (type_pset); type_pset = NULL; if (tree_queue_vec) VEC_free(tree, heap, tree_queue_vec); tree_queue_vec = NULL; if (!errorcount) dehydra_input_end (&dehydra); } #ifdef TREEHYDRA_PLUGIN static unsigned int execute_treehydra_pass (void) { gcc_plugin_pass(); return 0; } static struct opt_pass treehydra_pass = { .type = GIMPLE_PASS, .name = "treehydra", .gate = NULL, .execute = execute_treehydra_pass, .sub = NULL, .next = NULL, .static_pass_number = 0, .tv_id = 0, .properties_required = PROP_gimple_any, .properties_provided = 0, .properties_destroyed = 0, .todo_flags_start = 0, .todo_flags_finish = 0 }; #endif static tree handle_user_attribute (tree *node, tree name, tree args, int flags, bool *no_add_attrs) { return NULL_TREE; } static struct attribute_spec user_attr = { "user", 1, 1, false, false, false, handle_user_attribute }; static void gcc_plugin_attributes(void *_, void *_2) { register_attribute (&user_attr); // hack, plugin_init is called before aux_base_name is set // but it should be set by attribute registration dehydra_setFilename(&dehydra); } // FSF requires this crap to be put in int plugin_is_GPL_compatible = 1; int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) { char *pass_name = 0; if (!plugin_info->argc) return 1; if (flag_preprocess_only) return 0; int ret = gcc_plugin_init (plugin_info->full_name, plugin_info->argv, plugin_info->argc, &pass_name, version->basever); if (!ret) { // Look for a pass introduced in GCC 4.5 prunes useful info(class members/etc) & disable it #define DEFTIMEVAR(identifier__, name__) \ identifier__, enum { TV_NONE, #include "timevar.def" TIMEVAR_LAST }; struct opt_pass *p; for(p = all_small_ipa_passes;p;p=p->next) { if (p->tv_id != TV_IPA_FREE_LANG_DATA) continue; //disable it p->execute = NULL; break; } // It's gone now #ifdef TREEHYDRA_PLUGIN struct register_pass_info pass_info; pass_info.pass = &treehydra_pass; if (pass_name) { pass_info.reference_pass_name = pass_name; } else { pass_info.reference_pass_name = all_lowering_passes->name; } pass_info.ref_pass_instance_number = 0; pass_info.pos_op = PASS_POS_INSERT_AFTER; register_callback (plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); #endif register_callback (plugin_info->base_name, PLUGIN_FINISH_UNIT, gcc_plugin_post_parse, NULL); if (isGPlusPlus()) { register_callback (plugin_info->base_name, PLUGIN_PRE_GENERICIZE, (plugin_callback_func) gcc_plugin_cp_pre_genericize, NULL); } register_callback (plugin_info->base_name, PLUGIN_FINISH_TYPE, (plugin_callback_func) gcc_plugin_finish_struct, NULL); #ifdef CFG_FINISH_DECL if (!isGPlusPlus()) register_callback (plugin_info->base_name, PLUGIN_FINISH_DECL, gcc_plugin_finish_decl, NULL); #endif register_callback (plugin_info->base_name, PLUGIN_FINISH, gcc_plugin_finish, NULL); register_callback (plugin_info->base_name, PLUGIN_ATTRIBUTES, gcc_plugin_attributes, NULL); } return ret; } dehydra-89ed48e70997/dehydra_types.c0000644000000000000000000005012111757635752017175 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include "cp-tree-jsapi-workaround.h" #include #include #include #include #include "xassert.h" #include "dehydra_builtins.h" #include "util.h" #include "dehydra.h" #include "dehydra_ast.h" #include "dehydra_types.h" #include "jsval_map.h" static const char *POINTER = "isPointer"; static const char *REFERENCE = "isReference"; static const char *KIND = "kind"; static const char *TYPEDEF = "typedef"; static const char *ARRAY = "isArray"; static const char *INCOMPLETE = "isIncomplete"; static const char *ISVIRTUAL = "isVirtual"; static const char *ISTYPENAME = "isTypename"; static const char *VARIANT = "variantOf"; const char *ACCESS = "access"; const char *PUBLIC = "public"; const char *PRIVATE = "private"; const char *PROTECTED = "protected"; static struct jsval_map *typeMap = NULL; static const char *dehydra_typeString(tree type); static jsval dehydra_convert_type_cached (Dehydra *this, tree type, JSObject *obj); void dehydra_attachTypeAttributes (Dehydra *this, JSObject *obj, tree type) { JSObject *destArray = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, ATTRIBUTES, OBJECT_TO_JSVAL (destArray)); /* first add attributes from template */ tree decl_template_info = (isGPlusPlus() && TREE_CODE (type) == RECORD_TYPE) ? TYPE_TEMPLATE_INFO (type) : NULL_TREE; if (decl_template_info) { tree template_decl = TI_TEMPLATE (decl_template_info); tree type = TREE_TYPE (template_decl); tree attributes = TYPE_ATTRIBUTES (type); dehydra_addAttributes (this, destArray, attributes); } tree attributes = TYPE_ATTRIBUTES (type); dehydra_addAttributes (this, destArray, attributes); /* drop the attributes array if there are none */ if (! dehydra_getArrayLength (this, destArray)) { JS_DeleteProperty (this->cx, obj, ATTRIBUTES); } } static void dehydra_attachTypeTypedef(Dehydra *this, JSObject *obj, tree type) { tree type_decl = TYPE_NAME (type); if (!type_decl || TREE_CODE(type_decl) != TYPE_DECL) return; tree original_type = DECL_ORIGINAL_TYPE (type_decl); // bug 575398: circular typedef field generated with (typedefed) // attribute __aligned__ record_type if (original_type && type != original_type) { dehydra_defineStringProperty (this, obj, NAME, IDENTIFIER_POINTER(DECL_NAME(type_decl))); jsval subval = dehydra_convert_type (this, original_type); dehydra_defineProperty (this, obj, TYPEDEF, subval); dehydra_setLoc (this, obj, type_decl); } } static void dehydra_attachEnumStuff (Dehydra *this, JSObject *objEnum, tree enumeral_type) { JSObject *destArray = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineStringProperty (this, objEnum, KIND, "enum"); dehydra_defineProperty (this, objEnum, MEMBERS, OBJECT_TO_JSVAL(destArray)); tree tv; /* Output the list of possible values for the enumeration type. */ for (tv = TYPE_VALUES (enumeral_type); tv ; tv = TREE_CHAIN (tv)) { JSObject *obj = dehydra_addVar (this, NULL_TREE, destArray); dehydra_defineStringProperty (this, obj, NAME, IDENTIFIER_POINTER (TREE_PURPOSE (tv))); tree v = TREE_VALUE (tv); // GCC 4.4 switched to consts for enum values if (TREE_CODE (v) == CONST_DECL) v = DECL_INITIAL (v); int value = TREE_INT_CST_LOW (v); dehydra_defineProperty (this, obj, VALUE, INT_TO_JSVAL (value)); } } static void dehydra_addClassMember (Dehydra *this, tree decl, JSObject *memberArray) { JSObject *m = dehydra_addVar (this, decl, memberArray); dehydra_defineStringProperty (this, m, ACCESS, TREE_PRIVATE (decl) ? PRIVATE : (TREE_PROTECTED (decl) ? PROTECTED : PUBLIC)); } /* Note that this handles class|struct|union nodes */ static void dehydra_attachClassStuff (Dehydra *this, JSObject *objClass, tree record_type) { JSObject *destArray = JS_NewArrayObject (this->cx, 0, NULL); tree binfo = TYPE_BINFO (record_type); int n_baselinks = binfo ? BINFO_N_BASE_BINFOS (binfo) : 0; if (n_baselinks) dehydra_defineProperty (this, objClass, BASES, OBJECT_TO_JSVAL(destArray)); VEC(tree,gc) *accesses = binfo ? BINFO_BASE_ACCESSES (binfo) : 0; int i; for (i = 0; i < n_baselinks; i++) { JSObject *obj = JS_NewObject(this->cx, NULL, 0, 0); JS_DefineElement (this->cx, destArray, i, OBJECT_TO_JSVAL (obj), NULL, NULL, JSPROP_ENUMERATE); tree access = VEC_index (tree, accesses, i); dehydra_defineStringProperty (this, obj, ACCESS, IDENTIFIER_POINTER(access)); tree base_binfo = BINFO_BASE_BINFO (binfo, i); jsval base_type = dehydra_convert_type(this, BINFO_TYPE(base_binfo)); dehydra_defineProperty (this, obj, TYPE, base_type); if (BINFO_VIRTUAL_P(base_binfo)) dehydra_defineProperty (this, obj, ISVIRTUAL, JSVAL_TRUE); } destArray = JS_NewArrayObject(this->cx, 0, NULL); dehydra_defineProperty(this, objClass, MEMBERS, OBJECT_TO_JSVAL(destArray)); tree func; /* Output all the method declarations in the class. */ for (func = TYPE_METHODS (record_type) ; func ; func = TREE_CHAIN (func)) { if (DECL_ARTIFICIAL(func)) continue; /* Don't output the cloned functions. */ if (DECL_CLONED_FUNCTION_P (func)) continue; dehydra_addClassMember (this, func, destArray); } tree field; for (field = TYPE_FIELDS (record_type); field ; field = TREE_CHAIN (field)) { if (DECL_ARTIFICIAL(field) && !DECL_IMPLICIT_TYPEDEF_P(field)) continue; // ignore typedef of self field // my theory is that the guard above takes care of this one too if (TREE_CODE (field) == TYPE_DECL && TREE_TYPE (field) == record_type) continue; if (TREE_CODE (field) != FIELD_DECL) continue; dehydra_addClassMember (this, field, destArray); } dehydra_defineProperty (this, objClass, "size_of", INT_TO_JSVAL (tree_low_cst (TYPE_SIZE_UNIT (record_type), 1))); } static bool isAnonymousStruct(tree t) { tree name = TYPE_NAME (t); if (name && TREE_CODE (name) == TYPE_DECL) name = DECL_NAME (name); return !name || ANON_AGGRNAME_P (name); } static void dehydra_attachTemplateStuff (Dehydra *this, JSObject *parent, tree type) { if (!isGPlusPlus()) return; /* for reference see dump_aggr_type */ /* ugliest guard ever */ tree type_name = TYPE_NAME (type); bool decl_artificial = type_name ? DECL_ARTIFICIAL (type_name) : false; if (!(decl_artificial && TREE_CODE (type) != ENUMERAL_TYPE && TYPE_LANG_SPECIFIC (type) && CLASSTYPE_TEMPLATE_INFO (type) && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (type)) != TEMPLATE_DECL || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))))) { return; } tree tpl = CLASSTYPE_TI_TEMPLATE (type); if (!tpl) return; JSObject *obj = definePropertyObject(this->cx, parent, TEMPLATE, NULL, NULL, JSPROP_ENUMERATE); while (DECL_TEMPLATE_INFO (tpl)) tpl = DECL_TI_TEMPLATE (tpl); tree name = DECL_NAME (tpl); xassert (name); dehydra_defineStringProperty (this, obj, NAME, IDENTIFIER_POINTER (name)); tree info = TYPE_TEMPLATE_INFO (type); tree args = info ? TI_ARGS (info) : NULL_TREE; if (!args) return; if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args)) args = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1); int len = TREE_VEC_LENGTH (args); JSObject *arguments = JS_NewArrayObject (this->cx, len, NULL); dehydra_defineProperty (this, obj, ARGUMENTS, OBJECT_TO_JSVAL (arguments)); int ix; for (ix = 0; ix != len; ix++) { tree arg = TREE_VEC_ELT (args, ix); jsval val = JSVAL_VOID; if (TYPE_P (arg)) { val = dehydra_convert_type (this, arg); } else { JSString *str = JS_NewStringCopyZ (this->cx, expr_as_string (arg, 0)); val = STRING_TO_JSVAL (str); } xassert (val != JSVAL_VOID); JS_DefineElement(this->cx, arguments, ix, val, NULL, NULL, JSPROP_ENUMERATE); } } static void dehydra_attachClassName (Dehydra *this, JSObject *obj, tree type) { if (isAnonymousStruct (type)) { dehydra_defineProperty (this, obj, NAME, JSVAL_VOID); return; } dehydra_defineStringProperty (this, obj, NAME, dehydra_typeString(type)); } static void dehydra_convertAttachFunctionType (Dehydra *this, JSObject *obj, tree type) { tree arg_type = TYPE_ARG_TYPES (type); /* Skip "this" argument. */ if (TREE_CODE(type) == METHOD_TYPE) arg_type = TREE_CHAIN (arg_type); /* return type */ dehydra_defineProperty (this, obj, TYPE, dehydra_convert_type (this, TREE_TYPE (type))); JSObject *params = JS_NewArrayObject (this->cx, 0, NULL); JSObject *defaults = JS_NewArrayObject (this->cx, 0, NULL); dehydra_defineProperty (this, obj, PARAMETERS, OBJECT_TO_JSVAL (params)); JS_DefineProperty (this->cx, obj, HAS_DEFAULT, OBJECT_TO_JSVAL (defaults), NULL, NULL, 0); int i = 0; while (arg_type && (arg_type != void_list_node)) { JS_DefineElement(this->cx, params, i, dehydra_convert_type (this, TREE_VALUE (arg_type)), NULL, NULL, JSPROP_ENUMERATE); JS_DefineElement(this->cx, defaults, i, TREE_PURPOSE(arg_type) ? JSVAL_TRUE : JSVAL_FALSE, NULL, NULL, JSPROP_ENUMERATE); i++; arg_type = TREE_CHAIN (arg_type); } /* clear out defaults property if it's empty */ if (i == 0) { JS_DeleteProperty(this->cx, obj, HAS_DEFAULT); } } void dehydra_finishStruct (Dehydra *this, tree type) { if (!typeMap) return; jsval v; bool found = jsval_map_get(typeMap, type, &v); if (!found) return; xassert(JSVAL_IS_OBJECT(v)); JSObject *obj = JSVAL_TO_OBJECT(v); jsval incomplete = JSVAL_VOID; JS_GetProperty(this->cx, obj, INCOMPLETE, &incomplete); if (incomplete != JSVAL_TRUE) return; JS_DeleteProperty (this->cx, obj, INCOMPLETE); /* complete the type */ dehydra_convert_type_cached (this, type, obj); } static jsval dehydra_convert_type_cached (Dehydra *this, tree type, JSObject *obj) { xassert (TYPE_P(type)); tree context = TYPE_CONTEXT(type); if (context && TYPE_P(context)) { dehydra_defineProperty (this, obj, MEMBER_OF, dehydra_convert_type (this, context)); } tree variant = TYPE_MAIN_VARIANT(type); if (variant != type) { dehydra_defineProperty (this, obj, VARIANT, dehydra_convert_type (this, variant)); } int qualifiers = TYPE_QUALS (type); if (qualifiers & TYPE_QUAL_CONST) dehydra_defineProperty (this, obj, "isConst", JSVAL_TRUE); if (qualifiers & TYPE_QUAL_VOLATILE) dehydra_defineProperty (this, obj, "isVolatile", JSVAL_TRUE); if (qualifiers & TYPE_QUAL_RESTRICT) dehydra_defineProperty (this, obj, flag_isoc99 ? "restrict" : "__restrict__", JSVAL_TRUE); tree next_type = NULL_TREE; switch (TREE_CODE (type)) { case POINTER_TYPE: case OFFSET_TYPE: case REFERENCE_TYPE: dehydra_defineProperty (this, obj, TREE_CODE (type) == REFERENCE_TYPE ? REFERENCE : POINTER, JSVAL_TRUE); next_type = TREE_TYPE (type); dehydra_defineProperty(this, obj, PRECISION, INT_TO_JSVAL (TYPE_PRECISION (type))); break; case RECORD_TYPE: case UNION_TYPE: case ENUMERAL_TYPE: dehydra_defineStringProperty (this, obj, KIND, class_key_or_enum_as_string (type)); dehydra_attachClassName (this, obj, type); if (!COMPLETE_TYPE_P (type)) { /* need to do specal treatment for incomplete types Those need to be looked up later and finished */ dehydra_defineProperty (this, obj, INCOMPLETE, JSVAL_TRUE); } else if (TREE_CODE (type) == ENUMERAL_TYPE) { dehydra_attachEnumStuff (this, obj, type); dehydra_defineProperty(this, obj, UNSIGNED, TYPE_UNSIGNED (type) ? JSVAL_TRUE : JSVAL_FALSE); dehydra_defineProperty(this, obj, PRECISION, INT_TO_JSVAL (TYPE_PRECISION (type))); } else dehydra_attachClassStuff (this, obj, type); dehydra_attachTemplateStuff (this, obj, type); dehydra_setLoc (this, obj, type); break; case INTEGER_TYPE: case BOOLEAN_TYPE: { JSObject *tmp = this->destArray; this->destArray = JS_NewArrayObject (this->cx, 0, NULL); int key = dehydra_rootObject(this, OBJECT_TO_JSVAL (this->destArray)); tree type_min = TYPE_MIN_VALUE (type); // min/max may be missing in typecast expressions if (type_min) dehydra_makeVar(this, type_min, MIN_VALUE, obj); tree type_max = TYPE_MAX_VALUE (type); if (type_max) dehydra_makeVar(this, type_max, MAX_VALUE, obj); dehydra_unrootObject (this, key); this->destArray = tmp; } case REAL_TYPE: if (TYPE_UNSIGNED (type)) dehydra_defineProperty(this, obj, UNSIGNED, JSVAL_TRUE); else dehydra_defineProperty(this, obj, SIGNED, JSVAL_TRUE); dehydra_defineProperty(this, obj, PRECISION, INT_TO_JSVAL (TYPE_PRECISION (type))); case VOID_TYPE: #ifdef FIXED_POINT_TYPE_CHECK case FIXED_POINT_TYPE: #endif { /* The following code is ported from GCC c-pretty-print.c:pp_c_type_specifier */ tree type_decl = TYPE_NAME (type); if (type_decl) { dehydra_defineStringProperty (this, obj, NAME, IDENTIFIER_POINTER(TREE_CODE(type_decl) == TYPE_DECL ? DECL_NAME(type_decl) : type_decl)); } else { int prec = TYPE_PRECISION (type); dehydra_defineProperty (this, obj, BITFIELD, INT_TO_JSVAL (prec)); #ifdef ALL_FIXED_POINT_MODE_P if (ALL_FIXED_POINT_MODE_P (TYPE_MODE (type))) type = c_common_type_for_mode (TYPE_MODE (type), TYPE_SATURATING (type)); else #endif type = c_common_type_for_mode (TYPE_MODE (type), TYPE_UNSIGNED (type)); if (TYPE_NAME(type)) { dehydra_defineProperty(this, obj, "bitfieldOf", dehydra_convert_type(this, type)); const char *typeName = IDENTIFIER_POINTER(DECL_NAME(TYPE_NAME(type))); if (TYPE_PRECISION(type) != prec) { char *buf = xmalloc(strlen(typeName) + 40); sprintf(buf, "%s:%i", typeName, prec); dehydra_defineStringProperty(this, obj, NAME, buf); free(buf); } else { dehydra_defineStringProperty(this, obj, NAME, typeName); } } else { const char *typeName; char buf[100]; switch (TREE_CODE(type)) { case INTEGER_TYPE: typeName = TYPE_UNSIGNED(type) ? "unnamed-unsigned" : "unnamed-signed"; break; case REAL_TYPE: typeName = "unnamed-float"; break; #ifdef FIXED_POINT_TYPE_CHECK case FIXED_POINT_TYPE: typeName = "unnamed-fixed"; break; #endif default: gcc_unreachable(); } sprintf(buf, "<%s:%i>", typeName, prec); dehydra_defineStringProperty(this, obj, NAME, buf); } } break; } case COMPLEX_TYPE: case VECTOR_TYPE: /* maybe should add an isTemplateParam? */ case TEMPLATE_TYPE_PARM: case TYPENAME_TYPE: case TYPE_ARGUMENT_PACK: case TYPE_PACK_EXPANSION: // FIXME: variadic templates need better support dehydra_defineStringProperty (this, obj, NAME, dehydra_typeString(type)); dehydra_defineProperty (this, obj, ISTYPENAME, JSVAL_TRUE); break; case FUNCTION_TYPE: case METHOD_TYPE: dehydra_convertAttachFunctionType (this, obj, type); break; case ARRAY_TYPE: dehydra_defineProperty (this, obj, ARRAY, JSVAL_TRUE); if (TYPE_DOMAIN (type) && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) == INTEGER_CST) { dehydra_defineStringProperty (this, obj, MAX_VALUE, dehydra_intCstToString (TYPE_MAX_VALUE (TYPE_DOMAIN(type)))); } else { dehydra_defineProperty (this, obj, "variableLength", JSVAL_TRUE); } next_type = TREE_TYPE (type); break; case BOUND_TEMPLATE_TEMPLATE_PARM: case TEMPLATE_TEMPLATE_PARM: { // TODO: do something with TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO tree type_name = TYPE_NAME (type); if (type_name) { // what the hell else would a template be? xassert (DECL_P (type_name)); dehydra_defineStringProperty (this, obj, NAME, decl_as_string (type_name, 0)); break; } } case TYPEOF_TYPE: case DECLTYPE_TYPE: // avoid dealing with typeof mess dehydra_defineStringProperty (this, obj, "typeof_decltype_not_implemented", type_as_string (type, 0)); break; default: warning (1, "Dehydra: Unhandled %s: %s", tree_code_name[TREE_CODE(type)], type_as_string (type, TFF_CHASE_TYPEDEF)); dehydra_defineStringProperty (this, obj, NAME, type_as_string (type, 0)); break; } dehydra_attachTypeAttributes (this, obj, type); dehydra_attachTypeTypedef (this, obj, type); if (next_type != NULL_TREE) { dehydra_defineProperty (this, obj, TYPE, dehydra_convert_type (this, next_type)); } return OBJECT_TO_JSVAL (obj); } jsval dehydra_convert_type (Dehydra *this, tree type) { /* Not allowed to pass in NULLs this prevents nodes without types from haivng undefined .type*/ xassert (type); if (!typeMap) { typeMap = jsval_map_create (); } jsval v; bool found = jsval_map_get(typeMap, (void*)(intptr_t)TYPE_UID(type), &v); JSObject *obj = NULL; if (found) { jsval incomplete = JSVAL_VOID; xassert(JSVAL_IS_OBJECT(v)); obj = JSVAL_TO_OBJECT(v); JS_GetProperty(this->cx, obj, INCOMPLETE, &incomplete); /* add missing stuff to the type if it is now COMPLETE_TYPE_P */ if (incomplete == JSVAL_TRUE && COMPLETE_TYPE_P (type)) { JS_DeleteProperty (this->cx, obj, INCOMPLETE); } else { return v; } } else { obj = JS_NewObject (this->cx, &js_type_class, NULL, this->globalObj); jsval val = OBJECT_TO_JSVAL(obj); dehydra_rootObject (this, val); jsval_map_put(typeMap, (void*)(intptr_t)TYPE_UID(type), val); } return dehydra_convert_type_cached (this, type, obj); } /* Return a string name for the given type to be used as the NAME property. * * The design of this function is rather unfortunate. Pretty-printing * can get complicated with templates, and the GCC code isn't easy to * reuse, so we're going to call GCC and then fix up the results if * needed. */ static const char *dehydra_typeString(tree type) { const char *ans = type_as_string(type, 0); const char *chop_prefix = "const "; if (!strncmp(ans, chop_prefix, strlen(chop_prefix))) { ans += strlen(chop_prefix); } return ans; } dehydra-89ed48e70997/dehydra_types.h0000644000000000000000000000205211757635752017202 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DEHYDRA_TYPES_H #define DEHYDRA_TYPES_H void dehydra_finishStruct (Dehydra *this, tree type); jsval dehydra_convert_type (Dehydra *this, tree type); #endif dehydra-89ed48e70997/gcc_compat.h0000644000000000000000000000672411757635752016447 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef GCC_COMPAT_H #define GCC_COMPAT_H /* not sure if there is a better place to put these */ #pragma weak global_namespace #pragma weak cp_walk_subtrees #ifndef TREEHYDRA_GENERATED #if !defined(TREEHYDRA_CONVERT_JS) && !defined(TREEHYDRA_GENERATED) #include #endif #include #include #include #include #include #include "cp-tree-jsapi-workaround.h" #include #include #include #include #endif #ifndef cp_walk_tree_without_duplicates #define cp_walk_tree_without_duplicates walk_tree_without_duplicates #endif #ifndef GENERIC_TREE_OPERAND #define GENERIC_TREE_OPERAND TREE_OPERAND #endif #ifndef AGGR_INIT_EXPR_FN #define AGGR_INIT_EXPR_FN aggr_init_expr_fn static inline tree aggr_init_expr_fn(tree t) { tree fn = TREE_OPERAND (t, 0); if (TREE_CODE (fn) == ADDR_EXPR) fn = TREE_OPERAND (fn, 0); return fn; } #endif #ifndef aggr_init_expr_nargs #ifdef __APPLE_CC__ #define DEHYDRA_INPLACE_ARGS #else #error "inplace arguments are not supported in this environment" #endif static inline tree aggr_init_expr_args(tree t) { // see c-tree.texi: if AGGR_INIT_VIA_CTOR_P then the first argument in the // argument chain is not an argument but a VAR_DECL and equal to the // address of the third operand of AGGR_INIT_EXPR, so we'd rather skip it. tree args = TREE_OPERAND (t, 1); if (AGGR_INIT_VIA_CTOR_P (t)) args = TREE_CHAIN (args); return args; } #else static const int aggr_init_expr_first_param_index = 3; static inline tree aggr_init_expr_args(tree t) { return t; } static inline int aggr_init_expr_param_count(tree t) { return aggr_init_expr_nargs(t) + 3; } #endif #ifndef CALL_EXPR_FN #define CALL_EXPR_FN(t) TREE_OPERAND_CHECK_CODE (t, CALL_EXPR, 0) #ifndef DEHYDRA_INPLACE_ARGS #error "gcc and os combination you are compiling for is not supported" #endif static inline tree call_expr_args(tree t) { return TREE_OPERAND (t, 1); } static inline tree call_expr_first_arg(tree t) { // in apple gcc 42 the first arg is the value on the head of the chain // which is the first operand return TREE_VALUE (call_expr_args(t)); } static inline tree call_expr_rest_args(tree t) { return TREE_CHAIN (call_expr_args(t)); } #else static const int call_expr_first_param_index = 3; static inline tree call_expr_first_arg(tree t) { return GENERIC_TREE_OPERAND (t, call_expr_first_param_index); } static inline tree call_expr_rest_args(tree t) { return t; } #endif #ifndef TREE_OPERAND_LENGTH #define TREE_OPERAND_LENGTH(t) TREE_CODE_LENGTH (TREE_CODE (t)) #endif #endif dehydra-89ed48e70997/gcc_cp_headers.h0000644000000000000000000000360211757635752017251 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef GCC_CP_HEADERS #define GCC_CP_HEADERS /* Purpose of this header is to have a) Easily generated js bindings b) have a short way to include all of the needed headers b1) This file is included by generated code which can't stomach JS headers b2) This file is included by other code which does want JS headers */ #ifdef TREEHYDRA_CONVERT_JS #define GTY(x) __attribute__((user (("gty:"#x)))) #endif #if !defined(TREEHYDRA_CONVERT_JS) && !defined(TREEHYDRA_GENERATED) #include #endif #include #include #include #include #include #if defined(TREEHYDRA_CONVERT_JS) || defined(TREEHYDRA_GENERATED) /* this header conflicts with spidermonkey. sorry for above code */ #include #include #endif /*C++ headers*/ #include "cp-tree-jsapi-workaround.h" #include #include #include #include #include #include #endif dehydra-89ed48e70997/jsval_map.cc0000644000000000000000000000304611757635752016454 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include extern "C" { #include "jsval_map.h" typedef std::map Map; jsval_map* jsval_map_create() { Map* m = new Map; return reinterpret_cast (m); } void jsval_map_destroy(jsval_map* map) { Map* m = reinterpret_cast (map); delete m; } void jsval_map_put(jsval_map* map, void* k, jsval v) { Map* m = reinterpret_cast (map); m->insert(std::pair(k, v)); } bool jsval_map_get(jsval_map* map, void* k, jsval* ret) { Map* m = reinterpret_cast (map); Map::const_iterator it = m->find(k); if (it != m->end()) { *ret = it->second; return true; } return false; } } dehydra-89ed48e70997/jsval_map.h0000644000000000000000000000223211757635752016312 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef JSVAL_MAP_H #define JSVAL_MAP_H struct jsval_map; struct jsval_map *jsval_map_create(); void jsval_map_destroy(struct jsval_map *map); void jsval_map_put(struct jsval_map *map, void *k, jsval v); bool jsval_map_get(struct jsval_map *map, void *k, jsval *ret); #endif dehydra-89ed48e70997/libs/dehydra.js0000644000000000000000000002573311757635752017107 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include ("xhydra.js") Object.prototype.toString = function () {return uneval(this)} /* A clone function is required to clone state on block transitions if this one does not work for you, provide your own in your script */ function clone(myObj) { if(typeof(myObj) != 'object') return myObj; if(myObj.constructor == Array) { return myObj.map(clone) } var myNewObj = new myObj.constructor(); for(var i in myObj) myNewObj[i] = clone(myObj[i]); return myNewObj; } function structurally_equal(o1, o2) { const t1 = typeof o1 if(t1 != typeof o2) { return false } if(t1 != 'object') return o1 === o2; if(o1.constructor !== o2.constructor) return false; if(o1.constructor === Array && o1.length !== o2.length) { return false } // accumulate a union of properties var fields = new Array() // the following is done twice in case one is a superset of the other for(var i in o1) fields[i] = 0 for(var i in o2) fields[i] = 0 for(var i in fields) if(!structurally_equal(o1[i], o2[i])) return false return true } /* Uno-style programming support */ /* ALIAS address taken &symbol ARRAY_DECL appears as basename in an array declaration struct X symbol[8] DECL symbol appears as scalar in a declaration int symbol DEREF dereferenced *symbol FCALL used as name of function symbol(x) PARAM formal parameter of a function function(int symbol) { ... } REF0 dereferenced to access structure symbol->x REF1 used as structure name symbol.x REF2 used as structure element x->symbol, x.symbol USE evaluated to derive value x = symbol USEafterdef both DEF and USE, but USE occurs after DEF if ((symbol = x) > 0) USEbeforedef both DEF and USE, but USE occurs before DEF symbol++ _ ________________________________________________________________________________________ */ // The following are supported const FCALL = "isFcall", USE = "isUse", ALIAS = "isAlias" , DECL = "isDecl" , DEREF = "isDeref" , PARAM = "isArg", RETURN = "isReturn" // should change the following to be regexps // this allows to match by types function TYPE(x) { return "type=" + x } function PARAM_OF(x) { return "paramOf=" + x } function varField_matches (field, value,v) { if(!value.length) return true return value == v[field] } function varFlags_match (include, flags, x) { for(var f in flags) { const a = flags[f] const b = x[f] if(include) { if (a != b) return false } else if (a == b) { return false } } return true } function curry(func) { const new_args = Array.slice(arguments, 1, arguments.length) return function (a) { const args = new_args.concat(Array.slice(arguments)) return func.apply(this, args) } } function compose () { var funcs = Array.slice(arguments) return function () { var rval = funcs[funcs.length - 1].apply(this, arguments) for(var i = funcs.length - 2;i >= 0;i--) { rval = funcs[i](rval) } return rval } } function negate (f) { return function () { return !f.apply(this, arguments) } } /* @param include indicates whether return false if a member does not match or to return false if a member matches */ function flags2members(flags, include) { var members = new Array() for each (var f in flags) { var value = true var name = f const pos = f.indexOf('=') if(pos != -1) { name = f.substring(0, pos) value = f.substring(pos + 1) } members[name] = value } return members } function filterVars(vars, name, flags, nflags) { // pull out the magic type flags and stick them into type or the negation one, ntype var flags = flags2members(flags, true) if(name) flags["name"] = name var nflags = flags2members(nflags, false) return vars.filter(function(x) {return varFlags_match(true, flags, x)}).filter(function(x) {return varFlags_match(false, nflags, x)}) } /* Selects symbols from the current statement, based on their name or on def-use tags from the dataflow analysis. The call erases earlier results of selections. This is a boolean function that returns true if the resulting selection is nonempty, and otherwise returns false. */ function select(name, flags, nflags, v) { if (!v) v = _vars _selected = filterVars(v, name, flags, nflags) return _selected.length > 0 } /* Like select, but this time the selection refines the result of earlier selections, picking a subset of the symbols that match the additional criteria specified here. */ function refine(name, flags, nflags) { if(!_selected) return false _selected = filterVars(_selected, name, flags, nflags) return _selected.length > 0 } function display(vars) { print(uneval(vars)) } /* Reduces the current selection to those symbols that match the additional criteria and that have a specified (non-zero) mark, assigned through an earlier use of the mark primitive. */ function match(tag, flags, nflags) { if(!_state) return false const tagged = _state[tag] if(!tagged || !refine("", flags, nflags)) return false const filtered = _selected.filter( function (selectedV) { return tagged.some( function (v) { // return selectedV.name == v.name // id avoids trouble due to variables having the same name, but IDing code isn't finished yet return selectedV.id == v.id } ) } ) if(filtered.length > 0) { _selected = filtered return true } return false } /* Removes the markings for all currently selected symbols. */ function unmark(tag) { if(!_selected || !_state) return var removes = _selected.map(function (x) {return x.name}) for(var x in _state) { if(typeof tag != "undefined" && tag != x) continue _state[x] = _state[x].filter(function (v) { return removes.indexOf(v.name) == -1 }) } } /* Assigns an integer marking N to all currently selected symbols. Since the default marking is zero, the marking should be non-zero to be detectable later. The marking in effect assigns an individual state to a symbol of interest, so that the evolution of its state can be tracked and checked in the property. */ function mark(tag) { if(!_state) { _state = new Array() } if(! _state[tag]) { _state[tag] = _selected.slice() } else { var old = _state[tag] for each(var v in _selected) { old = old.filter(function(x) { x.id != v.id }) } _state[tag] = old.concat(_selected) } } /* Triggers the printing of a list of all symbols that appear on the depth-first search stack at the point of call, and all symbols that appear in the def-use list for the current statement. It also shows all symbols that were selected through use of the primitives select, refine, and unselect. */ function list() { if(typeof _selected == "object") { print("SELECTED:") display(_selected) } for(var i in _state) { print("MARKED " +i +":") display(_state[i]) } } /* Returns true if symbols exist that match the criteria specified, and that have the specified (non-zero) mark */ function marked(tag, flags, nflags) { if(!_state || !_state[tag]) return false _selected = filterVars(_state[tag], "", flags, nflags) return _selected.length > 0 } function known_zero() { if(!_selected) return false return _selected.every(function(x) {return is_zero(x.id)}) } function known_nonzero() { if(!_selected) return false return _selected.every(function(x) {return is_nonzero(x.id)}) } function known_global() { if(!_selected) return false return _selected.every(function(x) {return is_global(x.id)}) } /* Compacts fieldOf chains and/or produces shallow copies */ function flatten_item(item, copy) { if(item.fieldOf) { var new_item = flatten_item(item.fieldOf, true) for(var i in item) if(i != "fieldOf" && i != "name") new_item[i] = item[i] new_item.name += "." + item.name return new_item } else if (copy) { var new_item = new item.constructor() for(var i in item) new_item[i] = item[i] return new_item } return item } function extract(item) { var accum = [item] function extract_from(children) { for each (var v in item.arguments) { newv = flatten_item(v, true) newv.paramOf = item.name accum.push(extract(newv)); } } if(item.arguments) { extract_from(item.arguments) delete item.arguments } if(item.assign) { extract_from(item.assign) delete item.assign } return accum; } // couple use splice to optimize here function flatten_list(ls, dest) { if(!dest) dest = [] for each (var i in ls) if (i.constructor == Array) flatten_list(i, dest) else dest.push(i) return dest } /* converted the dehydra nested "ast" into uno-style flat list */ function flatten(lsa) { return flatten_list(lsa.map(function (x) {return extract(flatten_item(x, true))})); } function select_item(item) { item = flatten_item(item, true) delete item.arguments if(item.assign) { delete item.assign item.isDef = true } _selected = [item] return item } // recurses up to the source of the fcall function getVar(v) { var upper = v.fieldOf if(upper) return getVar(upper) return v } function iterate_vars(statements) { function var_iter(v) { yield v; for each (let va in v.assign) for each (let ret in var_iter(va)) yield ret; for each (let vp in v.arguments) for each (let ret in var_iter(vp)) yield ret; } for each (let o in statements) { this._loc = o.loc; for each (let v in o.statements) for each (let ret in var_iter(v)) yield ret; } } function process_function (decl, statements) { this._function = decl; if (!this.process) return; for each (let o in statements) { this._loc = o.loc; process(o.statements); } delete this._loc; } function sameVar (v1, v2) { // allow both to be undefined or both to be defined if (!v1 && !v2) return true; else if (! (v1 && v2)) return false; if (!(v1.name == v2.name && v1.type == v2.type)) return false; return sameVar(v1.fieldOf, v2.fieldOf); } function process_type (t) { if (this.process_class && (t.kind == "struct" || t.kind == "class")) { print ("Warning: process_class is deprecated. Use process_type") process_class (t) } } function DehydraDecl() { } function DehydraType() { } dehydra-89ed48e70997/libs/gcc_compat.js0000644000000000000000000005155511757635752017567 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Ports of GCC macros and functions. */ function TREE_CODE (tree) { return tree.tree_code() } function TREE_CODE_CLASS (code) { return tree_code_type[code.value] } function TREE_CODE_LENGTH (code) { return tree_code_length[code.value] } function IS_EXPR_CODE_CLASS (code_class) { return code_class.value >= tcc_reference.value && code_class.value <= tcc_expression.value } function IS_GIMPLE_STMT_CODE_CLASS (code_class) { return false; } function TreeCheckError (expected_code, actual_code) { var err = new Error("Expected " + expected_code + ", got " + actual_code) err.TreeCheckError = true return err } // comparable to CHECK_foo..except here foo is the second parameter function TREE_CHECK (tree_node, expected_code) { const code = TREE_CODE (tree_node) if (code != expected_code) { const a = TREE_CHECK.arguments for (var i = 2; i < a.length; i++) if (a[i] == code) return tree_node throw TreeCheckError(expected_code, TREE_CODE (tree_node)) } return tree_node } function TREE_CLASS_CHECK (node, expected_code) { const tree_code_class = TREE_CODE (node) if (TREE_CODE_CLASS (tree_code_class) != expected_code) throw Error("Expected " + expected_code + ", got " + TREE_CODE_CLASS (tree_code_class)) return node } function STATEMENT_LIST_HEAD(node) { return node.stmt_list.head } function VL_EXP_CLASS_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_vl_exp } function TREE_INT_CST (node) { return TREE_CHECK (node, INTEGER_CST).int_cst.int_cst } function TREE_INT_CST_LOW (node) { return TREE_INT_CST (node).low } function TREE_INT_CST_HIGH (node) { return TREE_INT_CST (node).high; } function tree_int_cst_sign(t) { if (TREE_INT_CST_LOW(t) == 0 && TREE_INT_CST_HIGH(t) == 0) return 0; if (TYPE_UNSIGNED(TREE_TYPE(t))) return 1; if (TREE_INT_CST_HIGH(t) < 0) return -1; return 1; } function VL_EXP_OPERAND_LENGTH (node) { return (TREE_INT_CST_LOW ((TREE_CLASS_CHECK (node, tcc_vl_exp).exp.operands[0]))) } function TREE_OPERAND_LENGTH (node) { if (VL_EXP_CLASS_P (node)) return VL_EXP_OPERAND_LENGTH (node); else return TREE_CODE_LENGTH (TREE_CODE (node)); } function GIMPLE_STMT_P (node) { return false; } // This macro was eventually removed in GCC 4.5, however this // is needed to simultaneously handle GENERIC and GIMPLE. function GIMPLE_TUPLE_P (node) { return node.hasOwnProperty("gsbase"); } function TREE_OPERAND (node, i) { return node.exp.operands[i] } var GIMPLE_STMT_OPERAND = gimple_op; function GENERIC_TREE_OPERAND (node, i) { if (GIMPLE_STMT_P (node)) return GIMPLE_STMT_OPERAND (node, i); return TREE_OPERAND (node, i); } function BIND_EXPR_VARS (node) { return TREE_OPERAND (TREE_CHECK (node, BIND_EXPR), 0) } function BIND_EXPR_BODY (node) { return TREE_OPERAND (TREE_CHECK (node, BIND_EXPR), 1) } const CALL_EXPR_FN_OPERAND_INDEX = 1; function CALL_EXPR_FN (node) { return TREE_OPERAND (TREE_CHECK (node, CALL_EXPR), CALL_EXPR_FN_OPERAND_INDEX) } function call_expr_arg_iterator (node) { let nargs = call_expr_nargs(node); for (let i = 0; i < nargs; ++i) yield CALL_EXPR_ARG(node, i); } function CALL_EXPR_ARG (node, i) { return TREE_OPERAND (TREE_CHECK (node, CALL_EXPR), i + 3); } function call_expr_nargs (node) { return TREE_INT_CST_LOW(TREE_OPERAND(node, 0)) - 3; } function TYPE_BINFO (node) { return TREE_CHECK (node, RECORD_TYPE, UNION_TYPE, QUAL_UNION_TYPE).type.binfo } function BINFO_BASE_ACCESSES(node) { return TREE_CHECK(node, TREE_BINFO).binfo.base_accesses; } function BINFO_VIRTUAL_P (node) { return TREE_CHECK(node, TREE_BINFO).base.static_flag; } // not sure if this will work same way in gcc 4.3 function TREE_CHAIN (node) { return node.common.chain } function TREE_VALUE (node) { return TREE_CHECK (node, TREE_LIST).list.value } function TREE_PURPOSE (node) { return node.list.purpose } function TREE_STRING_POINTER (node) { return node.string.str } function DECL_ATTRIBUTES (node) { return node.decl_common.attributes } function DECL_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_declaration } function DECL_INITIAL (node) { // can't do DECL_COMMON_CHECK return node.decl_common.initial } function DECL_SIZE (node) { // can't do DECL_COMMON_CHECK return node.decl_common.size } function DECL_SIZE_UNIT (node) { // can't do DECL_COMMON_CHECK return node.decl_common.size_unit } function DECL_NAME (node) { return node.decl_minimal.name } function DECL_UID (node) { return node.decl_minimal.uid } function DECL_CONTEXT (node) { return node.decl_minimal.context } function DECL_SAVED_TREE (node) { return TREE_CHECK (node, FUNCTION_DECL).decl_non_common.saved_tree } function DECL_STRUCT_FUNCTION(node) { return node.function_decl.f } // see gcc/tree.h for |enum symbol_visibility| definition: const VISIBILITY_DEFAULT = 0; const VISIBILITY_PROTECTED = 1; const VISIBILITY_HIDDEN = 2; const VISIBILITY_INTERNAL = 3; function DECL_VISIBILITY(node) { return node.decl_with_vis.visibility; } function DECL_VISIBILITY_SPECIFIED(node) { return node.decl_with_vis.visibility_specified; } function TREE_PRIVATE(node) { return node.base.private_flag; } function TREE_PROTECTED(node) { return node.base.protected_flag; } function IDENTIFIER_POINTER (node) { return node.identifier.id.str } function IDENTIFIER_OPNAME_P (node) { return !!node.base.lang_flag_2; } function IDENTIFIER_TYPENAME_P (node) { return !!node.base.lang_flag_4; } function TREE_VEC_LENGTH (node) { return TREE_CHECK (node, TREE_VEC).vec.length } function TREE_VEC_ELT (node, i) { return TREE_CHECK (node, TREE_VEC).vec.a[i] } function CONSTRUCTOR_ELTS (node) { return TREE_CHECK (node, CONSTRUCTOR).constructor.elts } function TREE_TYPE (node) { return node.common.type } function TYPE_OFFSET_BASETYPE(node) { return node.type.maxval } function TYPE_NAME (node) { return node.type.name } function TYPE_PRECISION (node) { return node.type.precision; } function SCALAR_FLOAT_TYPE_P(t) { return TREE_CODE(t) == REAL_TYPE; } function TYPE_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_type } function TYPE_CHECK (node) { return TREE_CLASS_CHECK(node, tcc_type); } function TYPE_MODE(node) { return node.type.mode; } function TYPE_LANG_SPECIFIC (node) { return node.type.lang_specific } function LANG_TYPE_IS_CLASS (node) { return TYPE_LANG_SPECIFIC(node).u.h.is_lang_type_class != 0; } function LANG_TYPE_CLASS_CHECK (node) { var lang_type = TYPE_LANG_SPECIFIC (node) if (!LANG_TYPE_IS_CLASS (node)) throw new Error ("Not a class!") return lang_type.u.c } function CLASSTYPE_TEMPLATE_INFO (node) { return LANG_TYPE_CLASS_CHECK(node).template_info } const TI_TEMPLATE = TREE_TYPE; function CLASSTYPE_TI_TEMPLATE (node) { return TI_TEMPLATE (CLASSTYPE_TEMPLATE_INFO (node)) } function DECL_LANG_SPECIFIC (node) { return node.decl_common.lang_specific } function decl_flags (tree) { return tree.u.fn; } function VAR_TEMPL_TYPE_OR_FUNCTION_DECL_CHECK(node) { return TREE_CHECK(node, VAR_DECL, FUNCTION_DECL, TYPE_DECL, TEMPLATE_DECL); } function DECL_TEMPLATE_INFO(node) { return DECL_LANG_SPECIFIC(VAR_TEMPL_TYPE_OR_FUNCTION_DECL_CHECK(node)).u.min.template_info; } function DECL_TI_TEMPLATE(node) { return TI_TEMPLATE (DECL_TEMPLATE_INFO (node)); } function TYPE_TEMPLATE_INFO(node) { if (TREE_CODE (node) == ENUMERAL_TYPE ) return ENUM_TEMPLATE_INFO (node) else if (TREE_CODE (node) == BOUND_TEMPLATE_TEMPLATE_PARM) return TEMPLATE_TEMPLATE_PARM_TEMPLATE_INFO (node) else if (TYPE_LANG_SPECIFIC (node)) return CLASSTYPE_TEMPLATE_INFO (node) } const TI_ARGS = TREE_CHAIN; function TMPL_ARGS_HAVE_MULTIPLE_LEVELS (node) { if (!node) return const elt = TREE_VEC_ELT (node, 0) return elt && TREE_CODE (elt) == TREE_VEC_ELT } const BINFO_TYPE = TREE_TYPE; function TYPE_METHODS(node) { return node.type.maxval; } /* This is so much simpler than the C version because it merely returns the vector array and lets the client for each it*/ // undefined is used for empty vectors, so support it nicely here. function VEC_iterate (vec_node) { return vec_node ? vec_node.base.vec : []; } function EXPR_P(node) { return IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (node))); } function CONSTANT_CLASS_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_constant; } function BINARY_CLASS_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_binary; } function UNARY_CLASS_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_unary; } function COMPARISON_CLASS_P (node) { return TREE_CODE_CLASS (TREE_CODE (node)) == tcc_comparison; } function INDIRECT_REF_P(node) { let code = TREE_CODE(node); return code == INDIRECT_REF || code == ALIGN_INDIRECT_REF || code == MISALIGNED_INDIRECT_REF; } function DECL_ARGUMENTS(tree) { return tree.decl_non_common.arguments; } function OBJ_TYPE_REF_EXPR(tree) { return TREE_OPERAND(tree, 0); } function OBJ_TYPE_REF_OBJECT(tree) { return TREE_OPERAND(tree, 1); } function OBJ_TYPE_REF_TOKEN(tree) { return TREE_OPERAND(tree, 2); } function BINFO_VIRTUALS(tree) { return tree.binfo.virtuals; } function BINFO_BASE_BINFOS(tree) { return tree.binfo.base_binfos; } const BV_FN = TREE_VALUE; function TYPE_ARG_TYPES(tree) { return tree.type.values; } function TYPE_METHOD_BASETYPE(tree) { return tree.type.maxval; } function TYPE_PTRMEMFUNC_P(tree) { return TREE_CODE(tree) == RECORD_TYPE && TYPE_LANG_SPECIFIC(tree) && TYPE_LANG_SPECIFIC(tree).u.c && TYPE_PTRMEMFUNC_FLAG(tree); } function TYPE_PTRMEMFUNC_FLAG(tree) { return TYPE_LANG_SPECIFIC(tree).u.c.ptrmemfunc_flag; } function TYPE_PTRMEMFUNC_FN_TYPE(NODE) { return TREE_TYPE (TYPE_FIELDS (NODE)); } function DECL_SOURCE_LOCATION(node) { return node.decl_minimal.locus; } function LOC_IS_BUILTIN(loc) { return loc._source_location <= 2; } function GIMPLE_STMT_LOCUS(tree) { return tree.gstmt.locus; } function EXPR_LOCATION(tree) { return tree.exp.locus; } let EDGE_TRUE_VALUE = 1024; let EDGE_FALSE_VALUE = 2048; function SWITCH_COND(expr) { return TREE_OPERAND(expr, 0); } function SWITCH_BODY(expr) { return TREE_OPERAND(expr, 1); } function SWITCH_LABELS(expr) { return TREE_OPERAND(expr, 2); } function CASE_LOW(expr) { return TREE_OPERAND(expr, 0); } function CASE_HIGH(expr) { return TREE_OPERAND(expr, 1); } function CASE_LABEL(expr) { return TREE_OPERAND(expr, 2); } function LABEL_DECL_UID(decl) { return decl.label_decl.label_decl_uid; } /** Given an OBJ_TYPE_REF, return a FUNCTION_DECL for the method. */ function resolve_virtual_fun_from_obj_type_ref(ref) { // This is a GCC macro definition, copied here for the use of this // function only. let TARGET_VTABLE_USES_DESCRIPTORS = 0; let obj_type = TREE_TYPE(OBJ_TYPE_REF_OBJECT(ref)); let index = OBJ_TYPE_REF_TOKEN(ref).int_cst.int_cst.low let fun = BINFO_VIRTUALS (TYPE_BINFO (TREE_TYPE (obj_type))); while (index) { fun = TREE_CHAIN (fun); index -= (TARGET_VTABLE_USES_DESCRIPTORS ? TARGET_VTABLE_USES_DESCRIPTORS : 1); } return BV_FN (fun); } /** True if the given FUNCTION_TYPE has stdargs. */ function stdarg_p(fntype) { let last; for (let a = TYPE_ARG_TYPES(fntype); a; a = TREE_CHAIN(a)) { last = TREE_VALUE(a); } return last != undefined && TREE_CODE(last) != VOID_TYPE; } let UNKNOWN_LOCATION = undefined; /** Return the source code location of the argument, which must be a tree. * The result is a JS object with 'file', 'line', and 'column' properties. */ function location_of(t) { if (GIMPLE_TUPLE_P(t)) return t.gsbase.location; if (TREE_CODE (t) == PARM_DECL && DECL_CONTEXT (t)) t = DECL_CONTEXT (t); else if (TYPE_P (t)) { t = TYPE_MAIN_DECL (t); /* pointer and reference types don't have a TYPE_MAIN_DECL, or a location */ if (!t) return UNKNOWN_LOCATION; } // TODO disabling this for now //else if (TREE_CODE (t) == OVERLOAD) // t = OVL_FUNCTION (t); if (t.tree_code() == TEMPLATE_PARM_INDEX) return UNKNOWN_LOCATION; if (DECL_P(t)) return DECL_SOURCE_LOCATION (t); else if (EXPR_P(t)) return EXPR_LOCATION(t); else if (GIMPLE_STMT_P(t)) return GIMPLE_STMT_LOCUS(t); else return UNKNOWN_LOCATION; } function DECL_ORIGINAL_TYPE (node) { return node.decl_non_common.result; } function TYPE_SIZE (node) { return node.type.size; } function TYPE_VALUES (node) { return node.type.values; } function COMPLETE_TYPE_P (node) { return TYPE_SIZE(node) != undefined; } function TYPE_VOLATILE (node) { return node.base.volatile_flag; } function TYPE_READONLY (node) { return node.base.readonly_flag; } function TYPE_RESTRICT (node) { return node.type.restrict_flag; } // flag representation of above let TYPE_UNQUALIFIED = 0; let TYPE_QUAL_CONST = 1; let TYPE_QUAL_VOLATILE = 2; let TYPE_QUAL_RESTRICT = 4; function TYPE_QUALS(NODE) { return ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)); } function CLASSTYPE_DECLARED_CLASS(node) { return LANG_TYPE_CLASS_CHECK(node).declared_class; } function LANG_DECL_U2_CHECK (node, tf) { let lt = DECL_LANG_SPECIFIC (node); if (decl_flags(lt).u2sel != tf) throw Error("LANG_DECL_U2_CHECK failed! expected: " + lt + " got: " + decl_flags(lt).u2sel); return decl_flags(lt).u2; } function DECL_ACCESS(node) { return LANG_DECL_U2_CHECK(node, 0).access; } function ANON_AGGRNAME_P(id_node) { return IDENTIFIER_POINTER(id_node).substr(0, 7) == '__anon_'; } function TYPE_FIELDS(node) { return node.type.values; } function DECL_ARTIFICIAL(node) { return node.decl_common.artificial_flag; } function TREE_STATIC(node) { return node.base.static_flag; } function TREE_PUBLIC(node) { return node.base.public_flag; } function DECL_IMPLICIT_TYPEDEF_P(node) { return TREE_CODE(node) == TYPE_DECL && DECL_LANG_FLAG_2(node); } function DECL_LANG_FLAG_2(node) { return node.decl_common.lang_flag_2; } function TYPE_ATTRIBUTES(node) { return node.type.attributes; } function TYPE_SIZE_UNIT(node) { return node.type.size_unit; } function TYPE_MAIN_DECL(NODE) { return TYPE_STUB_DECL (TYPE_MAIN_VARIANT (NODE)); } let TYPE_STUB_DECL = TREE_CHAIN; function TYPE_MAIN_VARIANT(node) { return TYPE_CHECK(node).type.main_variant; } function TYPE_NEXT_VARIANT(node) { return TYPE_CHECK(node).type.next_variant; } function DECL_CLONED_FUNCTION_P(node) { return (TREE_CODE(node) == FUNCTION_DECL || TREE_CODE(node) == TEMPLATE_DECL) && DECL_LANG_SPECIFIC(node) && !decl_flags(DECL_LANG_SPECIFIC(node)).thunk_p && DECL_CLONED_FUNCTION(node) != undefined; } function DECL_CLONED_FUNCTION(node) { return undefined; // TODO -- a whole bunch of union crap. do_dehydra_dump(DECL_LANG_SPECIFIC(node), 2, 1); return DECL_LANG_SPECIFIC(NON_THUNK_FUNCTION_CHECK(node)).u.f.u5.cloned_function; } // Just a bunch of debug checking in GCC. function NON_THUNK_FUNCTION_CHECK(node) { return node; } function DECL_CONSTRUCTOR_P(node) { return decl_flags(DECL_LANG_SPECIFIC(node)).constructor_attr; } function DECL_DESTRUCTOR_P(node) { return decl_flags(DECL_LANG_SPECIFIC(node)).destructor_attr; } function DECL_PURE_VIRTUAL_P(node) { return decl_flags(DECL_LANG_SPECIFIC(node)).pure_virtual; } function DECL_VIRTUAL_P(node) { return node.decl_common.virtual_flag; } function TYPE_DOMAIN(node) { return node.type.values; } function TYPE_MIN_VALUE(node) { return node.type.minval; } function TYPE_MAX_VALUE(node) { return node.type.maxval; } function DECL_NONSTATIC_MEMBER_FUNCTION_P(node) { return TREE_CODE(TREE_TYPE(node)) == METHOD_TYPE; } function CLASSTYPE_TI_ARGS(node) { return TI_ARGS(CLASSTYPE_TEMPLATE_INFO(node)); } function PRIMARY_TEMPLATE_P(node) { return DECL_PRIMARY_TEMPLATE(node) == node; } function DECL_PRIMARY_TEMPLATE(node) { return TREE_TYPE(DECL_INNERMOST_TEMPLATE_PARMS(node)); } function DECL_INNERMOST_TEMPLATE_PARMS(node) { return INNERMOST_TEMPLATE_PARMS(DECL_TEMPLATE_PARMS(node)); } function DECL_TEMPLATE_PARMS(node) { return node.decl_non_common.arguments; } function DECL_ASSEMBLER_NAME(node) { return node._decl_assembler_name.identifier; } function DECL_NONLOCAL(node) { return node.decl_common.nonlocal_flag } let INNERMOST_TEMPLATE_PARMS = TREE_VALUE; function class_key_or_enum_as_string(t) { if (TREE_CODE (t) == ENUMERAL_TYPE) return "enum"; if (TREE_CODE (t) == UNION_TYPE) return "union"; if (TYPE_LANG_SPECIFIC (t) && LANG_TYPE_IS_CLASS (t)) { if (CLASSTYPE_DECLARED_CLASS (t)) return "class"; return "struct"; } // C struct if (TREE_CODE (t) == RECORD_TYPE) return "struct"; return undefined; } function loc_as_string(loc) { return loc.file + ':' + loc.line + ':' + loc.column; } function TYPE_UNSIGNED(t) { return t.base.unsigned_flag; } /* The following globals are functions because they are tree nodes which must * not be held past the current treehydra callback. */ function char_type_node() sys.treehydra.gcc.integer_types[itk_char.value]; function signed_char_type_node() sys.treehydra.gcc.integer_types[itk_signed_char.value]; function unsigned_char_type_node() sys.treehydra.gcc.integer_types[itk_unsigned_char.value]; function short_integer_type_node() sys.treehydra.gcc.integer_types[itk_short.value]; function short_unsigned_type_node() sys.treehydra.gcc.integer_types[itk_unsigned_short.value]; function integer_type_node() sys.treehydra.gcc.integer_types[itk_int.value]; function unsigned_type_node() sys.treehydra.gcc.integer_types[itk_unsigned_int.value]; function long_integer_type_node() sys.treehydra.gcc.integer_types[itk_long.value]; function long_unsigned_type_node() sys.treehydra.gcc.integer_types[itk_unsigned_long.value]; function long_long_integer_type_node() sys.treehydra.gcc.integer_types[itk_long_long.value]; function long_long_unsigned_type_node() sys.treehydra.gcc.integer_types[itk_unsigned_long_long.value]; function c_common_type_for_mode (mode, unsignedp) { if (TYPE_MODE(integer_type_node()) == mode) return unsignedp ? unsigned_type_node() : integer_type_node(); if (TYPE_MODE(signed_char_type_node()) == mode) return unsignedp ? unsigned_char_type_node() : signed_char_type_node(); if (TYPE_MODE(short_integer_type_node()) == mode) return unsignedp ? short_unsigned_type_node() : short_integer_type_node(); if (TYPE_MODE(long_integer_type_node()) == mode) return unsignedp ? long_unsigned_type_node() : long_integer_type_node(); if (TYPE_MODE(long_long_integer_type_node()) == mode) return unsignedp ? long_long_unsigned_type_node() : long_long_integer_type_node(); return undefined; } // tuple stuff function gimple_code (gs) { return gs.gsbase.code } function gimple_location (gs) { return gs.gsbase.location; } function gimple_op(gs, i) { return gs.gimple_ops[i] } function gimple_call_fn (gs) { return gs.gimple_ops[1] } function gimple_call_lhs (gs) { return gimple_op (gs, 0); } function gimple_call_fndecl (gs) { let addr = gimple_call_fn (gs); if (TREE_CODE (addr) == ADDR_EXPR) { return TREE_OPERAND (addr, 0); } return undefined; } function gimple_num_ops (gs) { return gs.gsbase.num_ops; } function gimple_call_arg_iterator (gs) { let num_ops = gimple_num_ops (gs) for (let i = 3;i"; case VOID_TYPE: return "void"; case POINTER_TYPE: return type_string(TREE_TYPE(type)) + '*' + suffix; case ARRAY_TYPE: return type_string(TREE_TYPE(type)) + '[]' + suffix; case REFERENCE_TYPE: return type_string(TREE_TYPE(type)) + '&' + suffix; case FUNCTION_TYPE: return type_string(TREE_TYPE(type)) + ' (*)(' + type_args_string(type) + ')'; case METHOD_TYPE: // XXX exclude |this| from args? return type_string(TREE_TYPE(type)) + ' (' + type_string(TYPE_METHOD_BASETYPE(type)) + '::*)(' + type_args_string(type) + ')'; case OFFSET_TYPE: return type_string(TREE_TYPE(type)) + " " + type_string(TYPE_OFFSET_BASETYPE(type)) + "::*"; case VECTOR_TYPE: return prefix + "vector " + type_string(TREE_TYPE(type)) + "[" + (1 << TYPE_PRECISION(type)) + "]"; case RECORD_TYPE: case ENUMERAL_TYPE: case UNION_TYPE: // for these codes we should always have a TYPE_NAME. however, the C FE can be nasty // and not give us one in cases involving typedefs (bug 511261). for such cases, // we can make a guess... if (sys.frontend != "GNU C") throw new Error("TYPE_NAME null for " + code); // walk the list of variants for this type, and get the last variant with a valid TYPE_NAME. // this should represent the type we originated from. (we'll probably walk over the actual // TYPE_NAME we're after, too, but we've no way of knowing which one - any variants declared // after our one would appear first in the list, and we're better off walking toward the // source than away from it, for purposes of finding a consistent identifier.) for (let t = TYPE_MAIN_VARIANT(type); t && TYPE_NEXT_VARIANT(t); t = TYPE_NEXT_VARIANT(t)) if (TYPE_NAME(t)) type_decl = TYPE_NAME(t); if (TREE_CODE(type_decl) != TYPE_DECL) throw new Error("expected TYPE_DECL; original type is " + TREE_CODE(type_decl)); return prefix + decl_name_string(type_decl); case TYPE_PACK_EXPANSION: return type_string(TREE_TYPE(type)); case TYPE_DECL: case IDENTIFIER_NODE: throw new Error("Unexpected " + code); default: print(TREE_CODE(type).name); do_dehydra_dump(type.type, 1, 2); throw new Error("unhandled type type"); } } /** Return a string representation of the args part of a function type, * not including the parens. */ function type_args_string(type) { // GCC specialness: if the last item is void, then the function does // not use stdargs. Otherwise it does. let items = []; let stdarg = true; for (let o = TYPE_ARG_TYPES(type); o; o = TREE_CHAIN(o)) { let t = TREE_VALUE(o); if (TREE_CODE(t) == VOID_TYPE) { stdarg = false; break; } items.push(type_string(t)); } if (stdarg) items.push('...'); return items.join(', '); } /* Return a display representation of the given CFG instruction. */ function isn_display(isn) { let code = TREE_CODE(isn); let kind = rpad(code.name, 24); let [lhs, op, rhs] = isn_display_basic(isn, code); return kind + rpad(lhs, 8) + ' ' + rpad(op, 4) + rhs; } /** Helper for isn_display. This returns a representation without showing * the TREE_CODE and padding, i.e., the statement-type-specific part. */ function isn_display_basic(isn, code) { if (code == GIMPLE_MODIFY_STMT) { let operands = isn.operands(); return [expr_display(operands[0]), ":=", expr_display(operands[1])] } switch (TREE_CODE(isn)) { case RETURN_EXPR: let operand = isn.operands()[0]; if (operand) { return isn_display_basic(operand, TREE_CODE(operand)); } else { return [ 'void', '', '' ]; } case CALL_EXPR: return [ 'void', ':=', expr_display(isn) ]; case COND_EXPR: return [ 'if', '', expr_display(TREE_OPERAND(isn, 0)) ]; case SWITCH_EXPR: return [ 'switch', '', expr_display(TREE_OPERAND(isn, 0)) ]; case LABEL_EXPR: return [ decl_name(isn.operands()[0]) + ':', '', '' ]; default: return ['[this tree code not impl]', '', '']; } } /** Return a string representation of the given expression node. */ function expr_display(expr) { let code = TREE_CODE(expr); switch (code) { case INDIRECT_REF: return '*' + expr_display(expr.exp.operands[0]); case ARRAY_REF: return expr_display(TREE_OPERAND(expr, 0)) + '[' + expr_display(TREE_OPERAND(expr, 1)) + ']'; case ADDR_EXPR: return '&' + expr_display(TREE_OPERAND(expr, 0)); case TRUTH_NOT_EXPR: return '!' + expr_display(TREE_OPERAND(expr, 0)); case NOP_EXPR: return expr_display(TREE_OPERAND(expr, 0)); case PLUS_EXPR: case POINTER_PLUS_EXPR: return expr_display(TREE_OPERAND(expr, 0)) + ' + ' + expr_display(TREE_OPERAND(expr, 1)); case NE_EXPR: return expr_display(TREE_OPERAND(expr, 0)) + ' != ' + expr_display(TREE_OPERAND(expr, 1)); case EQ_EXPR: return expr_display(TREE_OPERAND(expr, 0)) + ' == ' + expr_display(TREE_OPERAND(expr, 1)); case CONVERT_EXPR: return '(' + type_string(TREE_TYPE(expr)) + ') ' + expr_display(expr.operands()[0]); case CALL_EXPR: let ans = ''; let name = CALL_EXPR_FN(expr); ans += call_name_display(name); ans += '('; let first = true; for (let operand in call_arg_iterator(expr)) { if (first) first = false; else ans += ', '; ans += expr_display(operand); } ans += ')'; return ans; case INTEGER_CST: return expr.int_cst.int_cst.low; case COMPONENT_REF: let ops = expr.operands() let struct = expr_display (ops[0]) if (TREE_CODE(ops[0]) == INDIRECT_REF) struct = "(" + struct + ")" return struct + "." + expr_display (ops[1]) default: if (DECL_P(expr)) { return decl_name(expr, true) } //print(" *** " + code.name); //do_dehydra_dump(expr, 4, 2); return code.name; } } /** Return a string representation the function being called in the * given CALL_EXPR function operand. */ function call_name_display(name) { switch (TREE_CODE(name)) { case ADDR_EXPR: return call_name_display(TREE_OPERAND(name, 0)); case VAR_DECL: case PARM_DECL: return '*' + decl_name_string(name); case FUNCTION_DECL: return decl_name(name); case OBJ_TYPE_REF: let func = resolve_virtual_fun_from_obj_type_ref(name); return call_name_display(func); default: throw new Error("call_name_display ni " + TREE_CODE(name).name); } } /** Dump the given CFG to stdout. */ function cfg_dump(cfg) { for (let bb in cfg_bb_iterator(cfg)) { print(bb_label(cfg, bb)); for each (let edge in VEC_iterate(bb.succs)) { let bbn = edge.dest; print(' -> ' + bb_label(cfg, bbn)); //do_dehydra_dump(bbn, 1, 1); } print(" statements:"); for (let isn in bb_isn_iterator(bb)) { print_indented(isn_display(isn), 2); /* let [c, o] = rectify_statement(isn); print_indented(c.name, 2); do_dehydra_dump(o, 2, 3); print(" --"); */ } } } /** Return a string representation of the given edge in the given CFG. */ function edge_string(edge, cfg) { return bb_label(cfg, edge.src) + ' -> ' + bb_label(cfg, edge.dest); } /** Return a string representation of a return value from location_of. * Use loc.toString() instead; this is for compatibility with existing * scripts. */ function loc_string(loc) { return loc.toString(); } function cond_code_display(code) { switch(code) { case LT_EXPR: return '<='; case LE_EXPR: return '<'; case GT_EXPR: return '>'; case GE_EXPR: return '>='; case EQ_EXPR: return '=='; case NE_EXPR: return '!='; default: throw new Error("Invalid conditional code value"); } } // tuple stuff function gs_display(gs) { switch(gs.gsbase.code) { case GIMPLE_CALL: { let fn = gimple_call_fndecl(gs); let lhs = gimple_call_lhs(gs); let args = [] for (a in gimple_call_arg_iterator(gs)) { args.push(expr_display(a)) } return (lhs ? call_name_display(lhs) + " = " : "") + call_name_display(fn) + "(" + args.join(",") +")" } break case GIMPLE_ASSIGN: return expr_display(gs.gimple_ops[0]) + " = " + expr_display(gs.gimple_ops[1]) case GIMPLE_RETURN: { let op = gs.gimple_ops[0]; return "return " + (op ? expr_display(gs.gimple_ops[0]) : "") } case GIMPLE_LABEL: return decl_name(gs.gimple_ops[0]) + ':' case GIMPLE_RESX: return "catch: //exception handler" case GIMPLE_COND: { let op = cond_code_display (gimple_cond_code (gs)); return "if ("+expr_display(gs.gimple_ops[0])+" " + op+ " " +expr_display(gs.gimple_ops[1])+")" } case GIMPLE_SWITCH: return "switch ("+expr_display(gs.gimple_ops[0])+")" default: return "Unhandled " + gs.gsbase.code } } dehydra-89ed48e70997/libs/gcc_util.js0000644000000000000000000003530511757635752017254 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Convenience functions for using GCC data in JS. */ // Extract the FUNCTION_DECL being called from the arg of a call site, // or a VAR_DECL/PARM_DECL if the value is a function pointer. // If a complex expression is being called (not a declaration), this // function will return null. function callable_arg_function_decl(arg) { switch (TREE_CODE(arg)) { case ADDR_EXPR: return callable_arg_function_decl(arg.operands()[0]); case FUNCTION_DECL: return arg; case OBJ_TYPE_REF: return resolve_virtual_fun_from_obj_type_ref(arg); case COMPONENT_REF: return arg.operands()[1]; case VAR_DECL: case PARM_DECL: return arg; default: if (!EXPR_P(arg)) warning("Unexpected argument to a CALL_EXPR: " + TREE_CODE(arg), location_of(arg)); return null; } } /** Return the string name of the given DECL. */ function decl_name_string(decl) { let name = DECL_NAME(decl); return name ? IDENTIFIER_POINTER(name) : ''; } /** Return an iterator over the args of a FUNCTION_TYPE. */ function function_type_args(fntype) { for (let a = TYPE_ARG_TYPES(fntype); a && TREE_CODE(TREE_VALUE(a)) != VOID_TYPE; a = TREE_CHAIN(a)) { yield TREE_VALUE(a); } } /** Return the CFG of a FUNCTION_DECL. */ function function_decl_cfg(tree) { return tree.function_decl.f.cfg; } /** Return the name of the given FUNCTION_DECL. */ let function_decl_name = decl_name_string; /** Return a string representation of the return type of the given * FUNCTION_DECL. */ function function_decl_return_type_name(tree) { let type = TREE_TYPE(TREE_TYPE(tree)); return type_string(type); } /** Return the expression returned by a given GIMPLE_RETURN or * RETURN_EXPR. Return undefined with void returns. */ function return_expr(exp) { TREE_CHECK(exp, RETURN_EXPR, GIMPLE_RETURN); let ret = exp.operands()[0]; if (!ret) return undefined; switch (ret.tree_code()) { case INIT_EXPR: case GIMPLE_MODIFY_STMT:{ let rhs = ret.operands()[1]; return rhs; } default: return ret; } } /** Yields all virtual member function decls in a node containing * expressions of the form &Class::foo, where foo is virtual. * We don't handle (frontend specific?) PTRMEM_CST nodes. */ function resolve_virtual_fn_addr_exprs(node) { switch (node.tree_code()) { case CONSTRUCTOR: switch (TREE_TYPE(node).tree_code()) { case ARRAY_TYPE: case RECORD_TYPE: case UNION_TYPE: for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(node))) { if (ce.index.tree_code() == FIELD_DECL && ce.value.tree_code() == INTEGER_CST && IDENTIFIER_POINTER(DECL_NAME(ce.index)) == "__pfn") { yield resolve_virtual_fn_addr_expr(ce.index, ce.value); } } break; case LANG_TYPE: break; default: throw new Error("Unexpected type in constructor: " + TREE_TYPE(node).tree_code()); } break; case GIMPLE_ASSIGN: let lhs = gimple_op(node, 0); if (lhs.tree_code() != COMPONENT_REF) return; let field_decl = TREE_OPERAND(lhs, 1) if (field_decl.tree_code() != FIELD_DECL) return; if (IDENTIFIER_POINTER(DECL_NAME(field_decl)) != "__pfn") return; let rhs = gimple_op(node, 1); if (rhs.tree_code() != INTEGER_CST) return; yield resolve_virtual_fn_addr_expr(field_decl, rhs); } } /** Helper for resolve_virtual_fn_addr_exprs */ function resolve_virtual_fn_addr_expr(pfn, vtable_index_tree) { let base = TYPE_METHOD_BASETYPE(TREE_TYPE(TYPE_PTRMEMFUNC_FN_TYPE(DECL_CONTEXT(pfn)))); let func = BINFO_VIRTUALS(TYPE_BINFO(base)); let ptr_size = TREE_INT_CST_LOW(TYPE_SIZE_UNIT(TREE_TYPE(vtable_index_tree))); let vtable_index = TREE_INT_CST_LOW(vtable_index_tree); while (vtable_index > 1) { func = TREE_CHAIN(func); vtable_index -= ptr_size; } return BV_FN(func); } /** Iterate over a functions local variables */ function local_decls_iterator(fndecl) { for (let list = DECL_STRUCT_FUNCTION(fndecl).local_decls; list; list = TREE_CHAIN(list)) yield TREE_VALUE(list); } /** Iterate over every statement of every block in the CFG. */ function cfg_isn_iterator(cfg) { for (let bb in cfg_bb_iterator(cfg)) for (let isn in bb_isn_iterator(bb)) yield isn; } /** Iterate over the basic blocks in the CFG. */ function cfg_bb_iterator(cfg) { let bb_entry = cfg.x_entry_block_ptr; let bb_exit = cfg.x_exit_block_ptr; let bb = bb_entry; while (true) { yield bb; if (bb === bb_exit) break; bb = bb.next_bb; } } /** Iterate over the statements in a STATEMENT_LIST */ function iter_statement_list(sl) { for (let ptr = STATEMENT_LIST_HEAD(sl); ptr; ptr = ptr.next) yield ptr.stmt; } // BB Access /** Return a text label for a BB. 'cfg' is optional, and is used to add (entry) and (exit) to * those blocks. */ function bb_label(cfg, bb) { // cfg param is optional if (bb == undefined) { bb = cfg; } if (bb == undefined) return "BB? (undefined)"; let s = 'BB' + bb.index; if (cfg) { if (bb === cfg.x_entry_block_ptr) s += ' (entry)'; else if (bb === cfg.x_exit_block_ptr) s += ' (exit)'; } return s; } /** Iterator over successor BBs of a BB. */ function bb_succs(bb) { for each (let edge in VEC_iterate(bb.succs)) { // Not sure why this happens, but it does seem to for exit blocks if (edge.dest != undefined) { yield edge.dest; } } } /** Iterator over outgoing edges of a BB. */ function bb_succ_edges(bb) { return VEC_iterate(bb.succs); } /** Iterator over predecessor BBs of a BB. */ function bb_preds(bb) { for each (let edge in VEC_iterate(bb.preds)) { yield edge.src; } } /** Iterator over incoming edges of a BB. */ function bb_pred_edges(bb) { return VEC_iterate(bb.preds); } /** stmt_list in a bb, may vary on gcc version. */ function bb_stmt_list(bb) { let iltree = bb.il.tree; return iltree ? iltree.stmt_list : undefined; } /** Iterate over statements of a BB. */ let bb_isn_iterator; /** Iterate over statements of a BB in reverse. */ let bb_isn_iterator_reverse; /** Last instruction of a BB. Throws an exception if there are no instructions. */ let bb_isn_last; // link_switches is a bit of a rough fit here but ok for now. /** Link up switches with the CFG by annotating the outgoing edges * with the case labels in a .case_val field, with null for default. * * Return a list of finally_tmp variables found. * * The purpose of this function is to allow analyses such as ESP to * track the conditions that hold on branches of a switch. */ let link_switches; function bb_gimple_seq(bb) { let gimple_bb_info = bb.il.gimple return gimple_bb_info ? gimple_bb_info.seq : undefined } bb_isn_iterator = function (bb) { let seq = bb_gimple_seq(bb) if (!seq) return for(let seq_node = seq.first;seq_node;seq_node=seq_node.next) yield seq_node.stmt } bb_isn_iterator_reverse = function (bb) { let seq = bb_gimple_seq(bb) if (!seq) return let seq_node = seq.last for(;seq_node;seq_node=seq_node.prev) yield seq_node.stmt } bb_isn_last = function (bb) { let seq = bb_gimple_seq(bb) return seq.last.stmt; } link_switches = function (cfg) { /** Helper for link_switches. */ function link_switch(cfg, bb, isn) { // GCC uses a default label for finally_tmp vars, even though there // is really only one possibility. We'll find it and put that in, // to help later analyses track branches. let label_count = gimple_switch_num_labels (isn) let max_val; for (i = 0; i < label_count; i++) { cl = gimple_switch_label (isn, i) let case_val = CASE_LOW(cl) == undefined ? null : TREE_INT_CST_LOW(CASE_LOW(cl)); if (case_val != null && (max_val == undefined || case_val > max_val)) max_val = case_val; if (is_finally_tmp(gimple_switch_index(isn)) && case_val == null) { case_val = max_val + 1; } let label_uid = LABEL_DECL_UID(CASE_LABEL(cl)); let bb_succ = cfg.x_label_to_block_map.base.vec[label_uid]; //print(label_uid + ' ' + bb_label(bb_succ)); let found = false; for each (let e in bb_succ_edges(bb)) { //print('BBS ' + e.dest.index + ' ' + bb_succ.index); if (e.dest == bb_succ) { e.case_val = case_val; found = true; //print('LINK ' + e.src.index + ' -> ' + e.dest.index + ": " + e.case_val); break; } } if (!found) throw new Error("not found"); } } let ans = []; for (let bb in cfg_bb_iterator(cfg)) { for (let isn in bb_isn_iterator(bb)) { if (gimple_code(isn) == GIMPLE_SWITCH) { let cond = gimple_switch_index(isn) if (is_finally_tmp(cond)) { ans.push(cond); } link_switch(cfg, bb, isn, ans); } } } return ans; } // Translate GCC FUNCTION_DECL to a pure JS object // Declaration translators /** Iterator over elements of a GCC TREE_CHAIN list representation. */ function flatten_chain(chain_head) { for (let o = chain_head; o; o = TREE_CHAIN(o)) { yield o; } } /** Return pure JS representation of arguments of an attribute. */ function rectify_attribute_args(tree) { let args = []; for (let a in flatten_chain(tree)) { let val = TREE_VALUE(a); args.push(TREE_CODE(val) == INTEGER_CST ? TREE_INT_CST_LOW(val) : TREE_STRING_POINTER(val)); } return args; } /** Return pure JS representation of attribtues of an AST node. */ function rectify_attributes(tree) { return [ { name: IDENTIFIER_POINTER(TREE_PURPOSE(attr)), args: rectify_attribute_args(TREE_VALUE(attr)) } for (attr in flatten_chain(tree)) ] } /** Return pure JS representation of a FUNCTION_DECL. */ function rectify_function_decl(tree) { return { name: decl_name_string(tree), resultType: function_decl_return_type_name(tree), params: [ { name: decl_name_string(o), type: type_string(TREE_TYPE(o)), attrs: rectify_attributes(DECL_ATTRIBUTES(o)) } for (o in flatten_chain(DECL_ARGUMENTS(tree))) ] }; } /** Return a string representation of output of rectify_function_decl. */ function rfunc_string(rfunc) { return rfunc.resultType + ' ' + rfunc.name + '(' + [ a.name + ' ' + a.type for each (a in rfunc.params) ].join(', ') + ')'; } /** Iterator over parameters of a FUNCTION_DECL. */ function function_decl_params(tree) { // void is used as a sentinel-type thing by GCC. return (v for (v in flatten_chain(DECL_ARGUMENTS(tree))) if (v && TREE_CODE(TREE_TYPE(v)) != VOID_TYPE)); } /** If the given node represents an integer literal expression, return * the value of the integer. Otherwise return undefined. */ function expr_literal_int(expr) { if (TREE_CODE(expr) == INTEGER_CST) { return TREE_INT_CST_LOW(expr); } else { return undefined; } } function call_function_decl(expr) { let fptr = CALL_EXPR_FN(expr); if (fptr.tree_code() != ADDR_EXPR) return undefined; let decl = TREE_OPERAND(fptr, 0); if (decl.tree_code() != FUNCTION_DECL) return undefined; return decl } /** The argument must be a CALL_EXPR. If it represents a call to a named * function (not a method or function pointer), then return the name. * Otherwise return undefined. */ function call_function_name(expr) { let decl = call_function_decl(expr) return decl ? decl_name_string(decl) : undefined; } /** The argument must be a GIMPLE_CALL. If it represents a call to a named * function (not a method or function pointer), then return the name. * Otherwise return undefined. */ function gimple_call_function_name(gs) { let decl = gimple_call_fndecl(gs); return decl ? decl_name_string(decl) : undefined; } /** Return the ith argument of the function, counting from zero and including * 'this' in the list if it is present. */ function call_arg(expr, i) { let arg_count = call_expr_nargs(expr); if (i < 0 || i >= arg_count) throw new Error("arg index out of range: call has " + arg_count + " args"); return CALL_EXPR_ARG(expr, i); } /** Iterator over the argument list of the function call. */ var call_arg_iterator = call_expr_arg_iterator; /** Return an array of the arguments of a CALL_EXPR. */ function call_expr_args(call_expr) { return [ a for (a in call_arg_iterator(call_expr)) ]; } /** Return an array of the arguments of a GIMPLE_CALL. */ function gimple_call_args(gimple_call) { return [ a for (a in gimple_call_arg_iterator(gimple_call)) ]; } /** Iterate over the base hierarchy of the given RECORD_TYPE in DFS order, * including the type itself. */ function dfs_base_iterator(record_type) { // Worklist of BINFOs. let work = [ TYPE_BINFO(record_type) ]; // Set of already-visited types -- We don't use decl_name_string here because the return // values would not be unique, although we shouldn't get any anyway. let done = MapFactory.create_set( function (x, y) DECL_UID(x) == DECL_UID(y), function (x) DECL_UID(x), function (t) IDENTIFIER_POINTER(DECL_NAME(TYPE_NAME(t))), function (t) type_string(t)); while (work.length > 0) { let binfo = work.pop(); let type = BINFO_TYPE(binfo); if (done.has(type)) continue; done.add(type); yield type; for each (let base in VEC_iterate(BINFO_BASE_BINFOS(binfo))) { work.push(base); } } } /** Return true if the given variable is a 'finally_tmp' variable, i.e., introduced * by GCC for a finally block (including destructors). */ function is_finally_tmp(decl) { let s = expr_display(decl); return s.substr(0, 11) == 'finally_tmp'; } function translate_attributes(atts) { return [{'name': IDENTIFIER_POINTER(TREE_PURPOSE(a)), 'value': [translate_attribute_param(TREE_VALUE(arg)) for (arg in flatten_chain(TREE_VALUE(a)))]} for (a in flatten_chain(atts))]; } function translate_attribute_param(param) { switch (param.tree_code()) { case STRING_CST: return TREE_STRING_POINTER(param); case INTEGER_CST: return TREE_INT_CST_LOW(param); case IDENTIFIER_NODE: return IDENTIFIER_POINTER(param); } } dehydra-89ed48e70997/libs/map.js0000644000000000000000000001720211757635752016234 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Map data structures /** Default equality function for maps. */ function base_equals(x, y) { return x === y; } /** Default key function for injective hash maps. */ function base_key(x) { return x.toString(); } /** Create a new Map with an ES4-like interface. */ function Map(equals_func, hashcode_func) { this.equals_func = equals_func ? equals_func : base_equals; this.hashcode_func = hashcode_func ? hashcode_func : hashcode; this.data = {}; } /** Create a new empty map with same customization functions as this one. */ Map.prototype.create = function() { return new Map(this.equals_func, this.hashcode_func); } /** Return a map that is a copy of this map: the map is distinct, but the * keys and values are pointer copies (not deep copies). */ Map.prototype.copy = function() { let m = this.create(); for (let [k, v] in this.getItems()) { m.put(k, v); } return m; }; /** Structural equality. Two maps are equal iff they have their key sets * are equal and the corresponding values are structurally equal. * @see equals (global). */ Map.prototype.equals = function(m) { for (let [k1, v1] in this.getItems()) { let v2 = m.get(k1); if (!equals(v1, v2)) return false; } for (let [k1, v1] in m.getItems()) { if (!this.has(k1)) return false; } return true; }; /** True if this map has no elements. */ Map.prototype.isEmpty = function() { for (let k in this.data) { return false; } return true; }; /** Add a key-value entry. */ Map.prototype.put = function(k, v) { let h = this.hashcode_func(k); let chain = this.data[h]; if (chain == undefined) { this.data[h] = [ [k, v] ]; return; } for (let i = 0; i < chain.length; ++i) { let entry = chain[i]; if (this.equals_func(entry[0], k)) { entry[1] = v; return; } } chain.push([k, v]); } /** Remove entry with given key. */ Map.prototype.remove = function(k) { let h = this.hashcode_func(k); let chain = this.data[h]; if (chain == undefined) return false; for (let i = 0; i < chain.length; ++i) { let entry = chain[i]; if (this.equals_func(entry[0], k)) { if (chain.length == 1) { delete this.data[h]; } else { chain.splice(i, 1); } return true; } } } /** Return the value associated with the given key, or undefined if none. */ Map.prototype.get = function(k) { let h = this.hashcode_func(k); let chain = this.data[h]; if (chain == undefined) return undefined; for (let i = 0; i < chain.length; ++i) { let entry = chain[i]; if (this.equals_func(entry[0], k)) { return entry[1]; } } return undefined; } /** True if this key is in the map. */ Map.prototype.has = function(k) { let h = this.hashcode_func(k); let chain = this.data[h]; if (chain == undefined) return false; for (let i = 0; i < chain.length; ++i) { let entry = chain[i]; if (this.equals_func(entry[0], k)) { return true; } } return false; } /** Iterate over key-value pairs. */ Map.prototype.getItems = function() { for each (let chain in this.data) { for (let i = 0; i < chain.length; ++i) { yield chain[i]; } } } /** Iterate over just the keys. */ Map.prototype.getKeys = function() { for each (let chain in this.data) { for (let i = 0; i < chain.length; ++i) { yield chain[i][0]; } } } /** Iterate over just the values. */ Map.prototype.getValues = function() { for each (let chain in this.data) { for (let i = 0; i < chain.length; ++i) { yield chain[i][1]; } } } /** Create a new map with custom behavior. * * @param keyFunc function key -> accessKey. This function must return the * "accessKey" for a key used in the map. The accessKey * defines equality of key objects, so must be unique for * keys that are to be considered distinct. * * @param labelFunc [optional] function value -> string. Used to print values * when printing the Map, in place to toString, which doesn't * work well for Treehydra objects. */ function InjHashMap(labelFunc, keyFunc) { this.getKey = keyFunc ? keyFunc : hashcode; this.getLabel = labelFunc == undefined ? function (s) s : labelFunc; this.data = {}; } /** Create a new empty map with same customization functions as this one. */ InjHashMap.prototype.create = function() { return new InjHashMap(this.getLabel, this.getKey); } /** Return a map that is a copy of this map: the map is distinct, but the * keys and values are pointer copies (not deep copies). */ InjHashMap.prototype.copy = function() { let m = this.create(); for (let [k, v] in this.getItems()) { m.put(k, v); } return m; }; /** Structural equality. Two maps are equal iff they have their key sets * are equal and the corresponding values are structurally equal. * @see equals (global). */ InjHashMap.prototype.equals = function(m) { for (let [k1, v1] in this.getItems()) { let v2 = m.get(k1); //print("MQ " + this.getKey(k1) + ' ' + this.getLabel(v1) + ' ' + this.getLabel(v1)); if (!equals(v1, v2)) return false; } for (let [k1, v1] in m.getItems()) { if (!this.has(k1)) return false; } return true; }; InjHashMap.prototype.toString = function() { let ss = [ this.getLabel(k) + ": " + v for ([k,v] in this.getItems()) ]; return Array.join(ss, ', '); }; /** True if this map has no elements. */ InjHashMap.prototype.isEmpty = function() { for (let k in this.data) { return false; } return true; }; /** Add a key-value entry. */ InjHashMap.prototype.put = function(k, v) { this.data[this.getKey(k)] = [k, v]; } /** Remove entry with given key. */ InjHashMap.prototype.remove = function(k) { let key = this.getKey(k) let ret = this.data.hasOwnProperty (key) if (!ret) return false delete this.data[key]; return true } /** Return the value associated with the given key, or undefined if none. */ InjHashMap.prototype.get = function(k) { let ak = this.getKey(k); // This is required for strict mode. if (!this.data.hasOwnProperty(ak)) return undefined; let [ko, vo] = this.data[ak]; return vo; } /** True if this key is in the map. */ InjHashMap.prototype.has = function(k) { return this.data[this.getKey(k)] != undefined; } /** Iterate over key-value pairs. */ InjHashMap.prototype.getItems = function() { for (let k in this.data) { yield this.data[k]; } } /** Iterate over just the keys. */ InjHashMap.prototype.getKeys = function() { for (let k in this.data) { let d = this.data[k]; // Hack because of _hashcode installed by treehydra.js if (typeof d != 'object') continue; yield d[0]; } } /** Iterate over just the values. */ InjHashMap.prototype.getValues = function() { for (let k in this.data) { let d = this.data[k]; // Hack because of _hashcode installed by treehydra.js if (typeof d != 'object') continue; yield d[1]; } } dehydra-89ed48e70997/libs/platform.js0000644000000000000000000000171611757635752017306 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ const isGCCApple = (sys.gcc_version.indexOf('Apple') != -1); const isGCC42 = false; const isGCC43 = false; const isUsingGCCTuples = true; dehydra-89ed48e70997/libs/set.js0000644000000000000000000000433411757635752016254 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include('map.js'); function create_set(map_ctor) { let Set = function(keyFunc, labelFunc) { this.map = new map_ctor(labelFunc, keyFunc); } /** Iterate over the items in the set. */ Set.prototype.items = function() { for (let e in this.map.getKeys()) { yield e; } }; /** Add an element to the set. */ Set.prototype.add = function(e) { this.map.put(e, e); }; /** Remove an element from the set. */ Set.prototype.remove = function(e) { this.map.remove(e); }; /** True if this element is in the set. */ Set.prototype.has = function(e) { return this.map.has(e); }; /** Add all elements in s2 to this set. */ Set.prototype.unionWith = function(s2) { for (let e in s2.items()) { this.map.put(e, e); } }; /** Shallow copy of this set. */ Set.prototype.copy = function() { let s = new Set(this.map.getKey, this.map.getLabel); s.unionWith(this); return s; }; /** String representation of the set. */ Set.prototype.toString = function() { let ss = [ this.map.getLabel(z) for (z in this.items()) ]; return Array.join(ss, ', '); }; Set.prototype.equals = function(m) { for (let k1 in this.items()) { if (!m.has(k1)) return false; } for (let k1 in m.items()) { if (!this.has(k1)) return false; } return true; }; return Set; } Set = create_set(Map); InjHashSet = create_set(InjHashMap); dehydra-89ed48e70997/libs/treehydra.js0000644000000000000000000002347411757635752017456 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include ("xhydra.js") include ("map.js") include ("platform.js") include ("gcc_compat.js") include ("gcc_util.js") include ("gcc_print.js") function unhandledLazyProperty (prop) { /* Special case: the interpreter will look for this property when * iterating. It doesn't appear in the prototype chain either, but * apparently that's fine--the interpreter will use default iterator * behavior, but only if we return true here. */ if (prop != "__iterator__") throw new Error("No " + prop + " in this lazy object") } EnumValue.prototype.toString = function () { return this.name } if (TREE_CODE) { include('enums.js') include('useful_arrays.js') } this.FILTER_EXPR = undefined; this.EXC_PTR_EXPR = undefined; this.GIMPLE_MODIFY_STMT = undefined; // Class that the lazyness builds itself around of function GCCNode () { } GCCNode.prototype.toString = function () { if (!this._struct_name) { if (this.common) { let ls = ["{tree_code:" + this.tree_code()] let name = this.toCString() if (name) ls.push ('toCString:"' + name + '"') let operands = this.operands if (operands) ls.push ("operands[" + operands.length + "]") ls.push ("}") return ls.join(" ") } } else if (this._struct_name == 'location_s') { return this.file + ':' + this.line } return "I am a " + this._struct_name } GCCNode.prototype.tree_code = function () { return GIMPLE_TUPLE_P(this) ? this.gsbase.code : this.base.code; } /* Convienient thing along the lines of GENERIC_TREE_OPERAND */ GCCNode.prototype.operands = function () { if (GIMPLE_TUPLE_P (this)) return this.gimple_ops; else if (GIMPLE_STMT_P (this)) return this.gstmt.operands; else if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (this.tree_code()))) return this.exp.operands; else throw new Error("no operands in this object"); } GCCNode.prototype.toSource = function () { throw new Error("Unevaling treehydra objects is unsupported"); } GCCNode.prototype.toCString = function () { if (DECL_P (this)) return decl_name(this) throw new Error("not implemented; only DECLs are supported"); } GCCNode.prototype.tree_check = function (expected_code) { return TREE_CHECK(this, expected_code) } function tree_stmt_iterator (ptr, container) { this.ptr = ptr this.container = container } function tsi_start (t) { return new tree_stmt_iterator (STATEMENT_LIST_HEAD (t), t); } // tsi_end_p tree_stmt_iterator.prototype.end = function () { return !this.ptr } // tsi_stmt_ptr (tree_stmt_iterator i) tree_stmt_iterator.prototype.stmt = function () { return this.ptr.stmt } //tsi_next (tree_stmt_iterator *i) tree_stmt_iterator.prototype.next = function () { this.ptr = this.ptr.next; } // func should "return" via throw // *walk_subtrees=0 is the same as returning false from func function walk_tree (t, func, guard, stack) { function WALK_SUBTREE (t) { walk_tree (t, func, guard, stack) } if (!t) return else if (guard) { if (guard.has (t)) return guard.put (t) } if (!stack) stack = []; var walk_subtrees = func (t, stack) if (walk_subtrees === false) return code = t.tree_code () stack.push (t) switch (code) { case TREE_LIST: WALK_SUBTREE (TREE_VALUE (t)); WALK_SUBTREE (TREE_CHAIN (t)); break; case TREE_VEC: { var len = TREE_VEC_LENGTH (t); while (len--) { WALK_SUBTREE (TREE_VEC_ELT (t, len)); } break; } case CONSTRUCTOR: for each (var ce in VEC_iterate (CONSTRUCTOR_ELTS (t))) { WALK_SUBTREE (ce.value); } break; case BIND_EXPR: { for (var decl = BIND_EXPR_VARS (t); decl; decl = TREE_CHAIN (decl)) { stack.push(decl); /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk into declarations that are just mentioned, rather than declared; they don't really belong to this part of the tree. And, we can see cycles: the initializer for a declaration can refer to the declaration itself. */ WALK_SUBTREE (DECL_INITIAL (decl)); WALK_SUBTREE (DECL_SIZE (decl)); WALK_SUBTREE (DECL_SIZE_UNIT (decl)); stack.pop(); } WALK_SUBTREE (BIND_EXPR_BODY (t)); break; } case STATEMENT_LIST: for (var i = tsi_start (t); !i.end (i); i.next()) { walk_tree (i.stmt (), func, guard, stack); } break; case TEMPLATE_PARM_INDEX: case PTRMEM_CST: case USING_DECL: case OVERLOAD: /* expressions with no operands */ break; default: if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)) || IS_GIMPLE_STMT_CODE_CLASS (TREE_CODE_CLASS (code))) { for each (let o in t.operands()) walk_tree (o, func, guard, stack) } else if (GIMPLE_TUPLE_P (t)) { for (let i = 0; i < gimple_num_ops(t); ++i) walk_tree(gimple_op(t, i), func, guard, stack); } } stack.pop () } /* prints a nice name for decls */ function decl_name (decl, no_context) { var str = ""; var context = no_context ? null : DECL_CONTEXT (decl) if (context) { var code = TREE_CODE (context) if (TYPE_P (context)) { context = TYPE_NAME (context) } if (code != FUNCTION_DECL && code != TRANSLATION_UNIT_DECL) { str = (context ? decl_name (context) : '') + "::" } } var name = DECL_NAME (decl) if (name) { str += IDENTIFIER_POINTER (name) } else { switch (TREE_CODE (decl)) { case CONST_DECL: str += 'C' break; case RESULT_DECL: str += 'R' break; default: str += 'D' } str += "_" + DECL_UID (decl) } return str } function pretty_walk (body, limit) { var counter = 0; function code_printer (t, depth) { if (++counter == limit) throw "done" var str = ""; for (var i = 0; i< depth.length;i++) str += " " var code = TREE_CODE (t) str += code if (code == OBJ_TYPE_REF) { t = resolve_virtual_fun_from_obj_type_ref(t) } if (DECL_P (t)) str += " " + decl_name (t); else { switch (code) { case STRING_CST: str += " \"" + TREE_STRING_POINTER(t) + "\""; break; case INTEGER_CST: str += " " + TREE_INT_CST_LOW(t) break; } } print (str) if (code == CONSTRUCTOR) { depth.push(t) for each (var ce in VEC_iterate (CONSTRUCTOR_ELTS (t))) { walk_tree (ce.index, code_printer, null, depth); // pretty_walk addition walk_tree (ce.value, code_printer, null, depth) } depth.pop() //indicate we don't need to visit subtrees return false; } } try { walk_tree (body, code_printer) } catch (e if e == "done") { } } function C_walk_tree_array() { return C_walk_tree().split("\n") } /* Gets the C printout of the current function body and ensures that the js traversal matches */ function sanity_check (body) { var c_walkls = C_walk_tree_array () var current_tree_node = 0; function checker (t, stack) { function bail_info () { // this causes C_walk_tree() to happen again, so lazy nodes and sequence_ns get along print ("C Walk:\n" + C_walk_tree_array().slice (0, current_tree_node+1).join("\n")) print ("JS Walk:") pretty_walk (b, current_tree_node + 1) if (stack.length) { var top = stack.pop() print ("Within " + TREE_CODE(top) + " seq:" + top.SEQUENCE_N) print ("Conflicted node seq:" + t.SEQUENCE_N) } } var code = TREE_CODE (t) var strname = tree_code_name[code.value] if (current_tree_node == c_walkls.length) { bail_info() throw new Error ("walk_tree made me walk more nodes than the C version. on " + code) } if (c_walkls[current_tree_node].indexOf(strname) != 0) { bail_info(); throw new Error ("walk_tree in C differs from JS, at " + current_tree_node + " expected " + c_walkls[current_tree_node] + " instead of " + strname) } ++current_tree_node; } walk_tree (body, checker, new Map()) if (current_tree_node != c_walkls.length) { throw Error("walk_tree didn't visit enough nodes, " + current_tree_node + " out of " + c_walkls.length + "visited") } } function fn_decl_body (function_decl) { body_chain = DECL_SAVED_TREE (function_decl) if (body_chain && TREE_CODE (body_chain) == BIND_EXPR) return BIND_EXPR_BODY (body_chain) return body_chain } // runs f against record_type and all base classes of it function walk_hierarchy (f, record_type) { var ret = f (record_type) if (ret) return ret; var binfo = TYPE_BINFO (record_type) for each (var base in VEC_iterate(BINFO_BASE_BINFOS (binfo))) { var ret = walk_hierarchy (f, BINFO_TYPE (base)) if (ret) return ret } return undefined; } function get_user_attribute (attributes) { for (var a = attributes; a; a = TREE_CHAIN (a)) { const name = IDENTIFIER_POINTER (TREE_PURPOSE (a)); if (name != "user") continue var args = TREE_VALUE (a); for (; args; args = TREE_CHAIN (args)) { return TREE_STRING_POINTER(TREE_VALUE(args)) } } return undefined; } dehydra-89ed48e70997/libs/unstable/BigInt.js0000644000000000000000000003161511757635752020454 0ustar rootroot00000000000000// BigInt.js - Arbitrary size integer math package for JavaScript // Copyright (C) 2000 Masanao Izumo // Version: 1.0.1 // Licence: GPL // // 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. // // // Interfaces: // x = new BigInt("1234567890123456789012345678901234567890"); // y = new BigInt("0x123456789abcdef0123456789abcdef0"); // z = x.clone(); // z = bigint_uminus(x); // z = bigint_plus(x, y); // z = bigint_minus(x, y); // z = bigint_mul(x, y); // z = bigint_div(x, y); // z = bigint_mod(x, y); // cmp = bigint_cmp(x, y); /* return -1, 0, or 1 */ // num = bigint_number(x); /* convert normal number (may be floating) */ // // Modifications: // // Add the .toString and .toStringBase functions to the BigInt prototype, instead of adding them // to each BigInt instance. function _BigInt_toString() { return this.toStringBase(10); } function _BigInt_toStringBase(base) { var i, j, hbase; var t; var ds; var c; i = this.len; if(i == 0) return "0"; if(i == 1 && !this.digits[0]) return "0"; switch(base) { default: case 10: j = Math.floor((2*8*i*241)/800)+2; hbase = 10000; break; case 16: j = Math.floor((2*8*i)/4)+2; hbase = 0x10000; break; case 8: j = (2*8*i)+2; hbase = 010000; break; case 2: j = (2*8*i)+2; hbase = 020; break; } t = this.clone(); ds = t.digits; s = ""; while (i && j) { var k = i; var num = 0; while (k--) { num = (num<<16) + ds[k]; if(num < 0) num += 4294967296; ds[k] = Math.floor(num / hbase); num %= hbase; } if (ds[i-1] == 0) i--; k = 4; while (k--) { c = (num % base); s = "0123456789abcdef".charAt(c) + s; --j; num = Math.floor(num / base); if (i == 0 && num == 0) { break; } } } i = 0; while(i < s.length && s.charAt(i) == "0") i++; if(i) s = s.substring(i, s.length); if(!this.sign) s = "-" + s; return s; } function _BigInt_clone() { var x, i; x = new BigInt(this.len, this.sign); for(i = 0; i < this.len; i++) x.digits[i] = this.digits[i]; return x; } function BigInt(len, sign) { var i, x, need_init; if(BigInt.arguments.length == 0) { this.sign = true; this.len = len = 1; this.digits = new Array(1); need_init = true; } else if(BigInt.arguments.length == 1) { x = bigint_from_any(BigInt.arguments[0]); if(x == BigInt.arguments[0]) x = x.clone(); this.sign = x.sign; this.len = x.len; this.digits = x.digits; need_init = false; } else { this.sign = (sign ? true : false); this.len = len; this.digits = new Array(len); need_init = true; } if(need_init) { for(i = 0; i < len; i++) this.digits[i] = 0; } } BigInt.prototype.toString = _BigInt_toString; BigInt.prototype.toStringBase = _BigInt_toStringBase; BigInt.prototype.clone = _BigInt_clone; function bigint_norm(x) { var len = x.len; var ds = x.digits; while(len-- && !ds[len]) ; x.len = ++len; return x; } function bigint_from_int(n) { var sign, big, i; if(n < 0) { n = -n; sign = false; } else sign = true; n &= 0x7fffffff; if(n <= 0xffff) { big = new BigInt(1, 1); big.digits[0] = n; } else { big = new BigInt(2, 1); big.digits[0] = (n & 0xffff); big.digits[1] = ((n>>16) & 0xffff); } return big; } function bigint_from_string(str, base) { var str_i; var sign = true; var c; var len; var z; var zds; var num; var i; var blen = 1; str += "@"; // Terminator; str_i = 0; // TODO: skip white spaces if(str.charAt(str_i) == "+") { str_i++; } else if (str.charAt(str_i) == "-") { str_i++; sign = false; } if (str.charAt(str_i) == "@") return null; if (!base) { if (str.charAt(str_i) == "0") { c = str.charAt(str_i + 1); if (c == "x" || c == "X") { base = 16; } else if (c == "b" || c == "B") { base = 2; } else { base = 8; } } else { base = 10; } } if (base == 8) { while (str.charAt(str_i) == "0") str_i++; len = 3 * (str.length - str_i); } else { // base == 10, 2 or 16 if (base == 16 && str.charAt(str_i) == '0' && (str.charAt(str_i+1) == "x" || str.charAt(str_i+1) == "X")) { str_i += 2; } if (base == 2 && str.charAt(str_i) == '0' && (str.charAt(str_i+1) == "b"||str.charAt(str_i+1) == "B")) { str_i += 2; } while (str.charAt(str_i) == "0") str_i++; if (str.charAt(str_i) == "@") str_i--; len = 4 * (str.length - str_i); } len = (len>>4)+1; z = new BigInt(len, sign); zds = z.digits; while(true) { c = str.charAt(str_i++); if(c == "@") break; switch (c) { case '0': c = 0; break; case '1': c = 1; break; case '2': c = 2; break; case '3': c = 3; break; case '4': c = 4; break; case '5': c = 5; break; case '6': c = 6; break; case '7': c = 7; break; case '8': c = 8; break; case '9': c = 9; break; case 'a': case 'A': c = 10; break; case 'b': case 'B': c = 11; break; case 'c': case 'C': c = 12; break; case 'd': case 'D': c = 13; break; case 'e': case 'E': c = 14; break; case 'f': case 'F': c = 15; break; default: c = base; break; } if (c >= base) break; i = 0; num = c; while(true) { while (i>>= 16; } if (num) { blen++; continue; } break; } } return bigint_norm(z); } function bigint_from_any(x) { if(typeof(x) == "object") { if(x.constructor == BigInt) return x; return BigInt(1, 1); } if(typeof(x) == "string") { return bigint_from_string(x); } if(typeof(x) == "number") { var i, x1, x2, fpt, np; if(-2147483647 <= x && x <= 2147483647) { return bigint_from_int(x); } x = x + ""; i = x.indexOf("e", 0); if(i == -1) return bigint_from_string(x); x1 = x.substr(0, i); x2 = x.substr(i+2, x.length - (i+2)); fpt = x1.indexOf(".", 0); if(fpt != -1) { np = x1.length - (fpt+1); x1 = x1.substr(0, fpt) + x1.substr(fpt+1, np); x2 = parseInt(x2) - np; } else { x2 = parseInt(x2); } while(x2-- > 0) { x1 += "0"; } return bigint_from_string(x1); } return BigInt(1, 1); } function bigint_uminus(x) { var z = x.clone(); z.sign = !z.sign; return bigint_norm(z); } function bigint_add_internal(x, y, sign) { var z; var num; var i, len; sign = (sign == y.sign); if (x.sign != sign) { if (sign) return bigint_sub_internal(y, x); return bigint_sub_internal(x, y); } if (x.len > y.len) { len = x.len + 1; z = x; x = y; y = z; } else { len = y.len + 1; } z = new BigInt(len, sign); len = x.len; for (i = 0, num = 0; i < len; i++) { num += x.digits[i] + y.digits[i]; z.digits[i] = (num & 0xffff); num >>>= 16; } len = y.len; while (num && i < len) { num += y.digits[i]; z.digits[i++] = (num & 0xffff); num >>>= 16; } while (i < len) { z.digits[i] = y.digits[i]; i++; } z.digits[i] = (num & 0xffff); return bigint_norm(z); // return z; } function bigint_sub_internal(x, y) { var z = 0; var zds; var num; var i; i = x.len; // if x is larger than y, swap if (x.len < y.len) { z = x; x = y; y = z; // swap x y } else if (x.len == y.len) { while (i > 0) { i--; if (x.digits[i] > y.digits[i]) { break; } if (x.digits[i] < y.digits[i]) { z = x; x = y; y = z; // swap x y break; } } } z = new BigInt(x.len, (z == 0) ? 1 : 0); zds = z.digits; for (i = 0, num = 0; i < y.len; i++) { num += x.digits[i] - y.digits[i]; zds[i] = (num & 0xffff); num >>>= 16; } while (num && i < x.len) { num += x.digits[i]; zds[i++] = (num & 0xffff); num >>>= 16; } while (i < x.len) { zds[i] = x.digits[i]; i++; } return bigint_norm(z); } function bigint_plus(x, y) { x = bigint_from_any(x); y = bigint_from_any(y); return bigint_add_internal(x, y, 1); } function bigint_minus(x, y) { x = bigint_from_any(x); y = bigint_from_any(y); return bigint_add_internal(x, y, 0); } function bigint_mul(x, y) { var i, j; var n = 0; var z; var zds, xds, yds; var dd, ee; var ylen; x = bigint_from_any(x); y = bigint_from_any(y); j = x.len + y.len + 1; z = new BigInt(j, x.sign == y.sign); xds = x.digits; yds = y.digits; zds = z.digits; ylen = y.len; while (j--) zds[j] = 0; for (i = 0; i < x.len; i++) { dd = xds[i]; if (dd == 0) continue; n = 0; for (j = 0; j < ylen; j++) { ee = n + dd * yds[j]; n = zds[i + j] + ee; if (ee) zds[i + j] = (n & 0xffff); n >>>= 16; } if (n) { zds[i + j] = n; } } return bigint_norm(z); } function bigint_divmod(x, y, modulo) { var nx = x.len; var ny = y.len; var i, j; var yy, z; var xds, yds, zds, tds; var t2; var num; var dd, q; var ee; var mod, div; yds = y.digits; if (ny == 0 && yds[0] == 0) return null; // Division by zero if (nx < ny || nx == ny && x.digits[nx - 1] < y.digits[ny - 1]) { if (modulo) return bigint_norm(x); return BigInt(1, 1); } xds = x.digits; if (ny == 1) { dd = yds[0]; z = x.clone(); zds = z.digits; t2 = 0; i = nx; while (i--) { t2 = t2 * 65536 + zds[i]; zds[i] = (t2 / dd) & 0xffff; t2 %= dd; } z.sign = (x.sign == y.sign); if (modulo) { if (!x.sign) t2 = -t2; if (x.sign != y.sign) { t2 = t2 + yds[0] * (y.sign ? 1 : -1); } return bigint_from_int(t2); } return bigint_norm(z); } z = new BigInt(nx == ny ? nx + 2 : nx + 1, x.sign == y.sign); zds = z.digits; if (nx == ny) zds[nx + 1] = 0; while (!yds[ny - 1]) ny--; if ((dd = ((65536/(yds[ny-1]+1)) & 0xffff)) != 1) { yy = y.clone(); tds = yy.digits; j = 0; num = 0; while (j>= 16; } yds = tds; j = 0; num = 0; while (j>= 16; } zds[j] = num & 0xffff; } else { zds[nx] = 0; j = nx; while (j--) zds[j] = xds[j]; } j = nx==ny?nx+1:nx; do { if (zds[j] == yds[ny-1]) q = 65535; else q = ((zds[j]*65536 + zds[j-1])/yds[ny-1]) & 0xffff; if (q) { i = 0; num = 0; t2 = 0; do { // multiply and subtract t2 += yds[i] * q; ee = num - (t2 & 0xffff); num = zds[j - ny + i] + ee; if (ee) zds[j - ny + i] = num & 0xffff; num >>= 16; t2 >>>= 16; } while (++i < ny); num += zds[j - ny + i] - t2; // borrow from high digit; don't update while (num) { // "add back" required i = 0; num = 0; q--; do { ee = num + yds[i]; num = zds[j - ny + i] + ee; if (ee) zds[j - ny + i] = num & 0xffff; num >>= 16; } while (++i < ny); num--; } } zds[j] = q; } while (--j >= ny); if (modulo) { // just normalize remainder mod = z.clone(); if (dd) { zds = mod.digits; t2 = 0; i = ny; while (i--) { t2 = (t2*65536) + zds[i]; zds[i] = (t2 / dd) & 0xffff; t2 %= dd; } } mod.len = ny; mod.sign = x.sign; if (x.sign != y.sign) { return bigint_add_internal(mod, y, 1); } return bigint_norm(mod); } div = z.clone(); zds = div.digits; j = (nx==ny ? nx+2 : nx+1) - ny; for (i = 0;i < j;i++) zds[i] = zds[i+ny]; div.len = i; return bigint_norm(div); } function bigint_div(x, y) { x = bigint_from_any(x); y = bigint_from_any(y); return bigint_divmod(x, y, 0); } function bigint_mod(x, y) { x = bigint_from_any(x); y = bigint_from_any(y); return bigint_divmod(x, y, 1); } function bigint_cmp(x, y) { var xlen; if(x == y) return 0; // Same object x = bigint_from_any(x); y = bigint_from_any(y); xlen = x.len; if(x.sign != y.sign) { if(x.sign) return 1; return -1; } if (xlen < y.len) return (x.sign) ? -1 : 1; if (xlen > y.len) return (x.sign) ? 1 : -1; while(xlen-- && (x.digits[xlen] == y.digits[xlen])) ; if (-1 == xlen) return 0; return (x.digits[xlen] > y.digits[xlen]) ? (x.sign ? 1 : -1) : (x.sign ? -1 : 1); } function bigint_number(x) { var d = 0.0; var i = x.len; var ds = x.digits; while (i--) { d = ds[i] + 65536.0 * d; } if (!x.sign) d = -d; return d; } dehydra-89ed48e70997/libs/unstable/adts.js0000644000000000000000000000665611757635752020242 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Data structures for program analysis. */ include('map.js'); include('set.js'); // Speed measurements let timers = {}; function timer_start(name) { if (name == undefined) name = 'default'; timers[name] = new Date(); } function timer_stop(name) { if (name == undefined) name = 'default'; let t1 = new Date(); let t0 = timers[name]; let dt = t1-t0; print("timer '" + name + "': " + dt); } // Equality function /** Structural equality function. Return true if the two objects are * equal. Equality of arrays is defined as equality of corresponding * elements. Equality of other objects is defined by calling * v1.equals(v2). Equality of all other values is according to ==. * * This function works with Maps and Sets. */ function equals(v1, v2) { if (v1 instanceof Object && v2 instanceof Object) { if (v1 instanceof Array && v2 instanceof Array) { if (v1.length != v2.length) return false; for (let i = 0; i < v1.length; ++i) { if (!equals(v1[i], v2[i])) return false; } return true; } else if (v1.equals) { return v1.equals(v2); } } return v1 == v2; } function test_perf() { let m = new Map(function (x, y) x === y, function(x) x); do_test_perf(m, 'Map'); m = new InjHashMap(undefined, function(x) x); do_test_perf(m, 'InjHashMap'); let s = new Set(function (x, y) x === y, function(x) x); do_test_perf_set(s, 'Set'); s = new InjHashSet(function(x) x); do_test_perf_set(s, 'InjHashSet'); } function do_test_perf(m, tag) { let size = 100000; timer_start(tag); for (let i = 0; i < size; ++i) { m.put(i, i * 3); } for (let j = 0; j < 3; ++j) { for (let i = 0; i < size; ++i) { m.get(i); } } timer_stop(tag); } function do_test_perf_set(s, tag) { let size = 100000; timer_start(tag); for (let i = 0; i < size; ++i) { s.add(i * 3); } for (let j = 0; j < 3; ++j) { for (let i = 0; i < size; ++i) { s.has(i); } } timer_stop(tag); } //test_perf(); /** Singleton. */ function MapFactoryCtor() { this.use_injective = false; } MapFactoryCtor.prototype.create_map = function(equals_func, hash_func, key_func) { if (this.use_injective) { return new InjHashMap(undefined, key_func); } else { return new Map(equals_func, hash_func); } } MapFactoryCtor.prototype.create_set = function(equals_func, hash_func, key_func) { if (this.use_injective) { return new InjHashSet(key_func); } else { return new Set(equals_func, hash_func); } } /** Factory for maps and sets. Allows the user to globally configure which * implementation is used. */ MapFactory = new MapFactoryCtor(); dehydra-89ed48e70997/libs/unstable/analysis.js0000644000000000000000000002623411757635752021124 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Program Analysis Library */ /* Analsis-specialized ADTs */ function decl_equals(x, y) { return DECL_UID(x) == DECL_UID(y); } function decl_hashcode(x) { return DECL_UID(x); } /** Return a Set specialized for DECL keys. */ function create_decl_set(items) { let ans = MapFactory.create_set(decl_equals, decl_hashcode, decl_get_key, decl_get_label); if (items != undefined) { for each (let item in items) { ans.add(item); } } return ans; } /** Return a Map with DECL keys. */ function create_decl_map() { return MapFactory.create_map(decl_equals, decl_hashcode, decl_get_key, decl_get_label); } /** Return a key usable for Maps with DECLs. */ function decl_get_key(d) { return DECL_UID(d); } /** Label function suitable for printing DECLs. */ function decl_get_label(d) { return expr_display(d); } /* Analysis-related accessors */ function defs_filter(ls) { for each(let d in ls) { if (!d) continue; // ignore |this| if (d.tree_code() == COMPONENT_REF) d = TREE_OPERAND(d, 1); try { walk_tree(d, function (t) { if (DECL_P(t)) throw t; }); } catch (t) { yield t; } } } function uses_filter(ls) { for each(let d in ls) { if (!d) continue; try { walk_tree(d, function (t) { if (DECL_P(t)) throw t; }); } catch (t) { yield t; } } } /** Iterate over variables defined by the instruction. If kind == 'strong', iterate over only strong defs. */ function isn_defs(isn, kind) { let retls = [] switch (gimple_code(isn)) { case GIMPLE_ASSIGN: retls.push(isn.gimple_ops[0]); if (kind != 'strong') { retls.push(isn.gimple_ops[1]); retls.push(isn.gimple_ops[2]); } break; case GIMPLE_CALL: { retls.push(gimple_call_lhs(isn)); if (kind != 'strong') { for (let d in gimple_call_arg_iterator(isn)) retls.push(d); } } break; case GIMPLE_RETURN: if (kind != 'strong') { let operand = isn.gimple_ops[0]; retls.push(operand); } break; case GIMPLE_COND: case GIMPLE_SWITCH: break; default: throw new Error("isn_defs ni " + gimple_code(isn)); } for each(let d in defs_filter(retls)) yield d; }; /** Iterate over variables used by the instruction. */ function isn_uses(isn) { let retls = []; switch (gimple_code(isn)) { case GIMPLE_ASSIGN: retls.push(isn.gimple_ops[0]); retls.push(isn.gimple_ops[1]); break; case GIMPLE_CALL: for (let d in gimple_call_arg_iterator(isn)) retls.push(d); break; case GIMPLE_RETURN: retls.push(isn.gimple_ops[0]); break; case GIMPLE_COND: retls.push(isn.gimple_ops[0]); retls.push(isn.gimple_ops[1]); break; case GIMPLE_SWITCH: retls.push(isn.gimple_ops[0]); break; default: throw new Error("ni " + gimple_code(isn)); } for each(let d in uses_filter(retls)) yield d; }; /** Iterate over variables defined by a call. All of these are weak defs. * This is unsound -- we just iterate over x where &x is an arg, without * attempting alias analysis. */ function call_expr_defs(isn) { let operand_count = TREE_INT_CST(TREE_OPERAND(isn, 0)).low; let arg_count = operand_count - 3; for (let i = 0; i < arg_count; ++i) { let operand_index = i + 3; let operand = TREE_OPERAND(isn, operand_index); if (TREE_CODE(operand) == ADDR_EXPR) { yield TREE_OPERAND(operand, 0); } } }; /** Iterate over variables used by an expression node. */ function expr_uses(expr) { if (DECL_P(expr)) { yield expr; } else if (CONSTANT_CLASS_P(expr)) { // no uses } else if (BINARY_CLASS_P(expr) || COMPARISON_CLASS_P(expr)) { yield TREE_OPERAND(expr, 0); yield TREE_OPERAND(expr, 1); } else if (UNARY_CLASS_P(expr)) { yield TREE_OPERAND(expr, 0); } else { switch (TREE_CODE(expr)) { case TRUTH_NOT_EXPR: case VIEW_CONVERT_EXPR: yield TREE_OPERAND(expr, 0); break; case TRUTH_XOR_EXPR: case TRUTH_AND_EXPR: case TRUTH_OR_EXPR: yield TREE_OPERAND(expr, 0); yield TREE_OPERAND(expr, 1); break; case INDIRECT_REF: yield expr; yield TREE_OPERAND(expr, 0); break; case ADDR_EXPR: // The value is not really being used here, but we'll pretend it is, // as having the address taken means it could be later on. yield TREE_OPERAND(expr, 0); break; case COMPONENT_REF: yield TREE_OPERAND(expr, 0); break; case ARRAY_REF: yield TREE_OPERAND(expr, 0); yield TREE_OPERAND(expr, 1); break; case BIT_FIELD_REF: yield TREE_OPERAND(expr, 0); break; case CALL_EXPR: for (let v in call_expr_uses(expr)) yield v; break; case EXC_PTR_EXPR: case FILTER_EXPR: // Ignore exception handling for now break; case CONSTRUCTOR: // From what we've seen so far, in C++ this appears w/o args break; default: throw new Error("expr_uses ni " + TREE_CODE(expr).name); } } } /** Iterate over variables used by a CALL_EXPR. */ var call_expr_uses = call_expr_arg_iterator; /* General analysis functions */ /** Return an array of the basic blocks of the cfg in postorder. */ function postorder(cfg) { let order = new Array(); do_postorder(cfg.x_entry_block_ptr, order); for each (let bb in order) delete bb._visited; return order; } /** Helper for postorder. */ function do_postorder(bb, order) { if (bb._visited) return; bb._visited = true; for (let bb_succ in bb_succs(bb)) { do_postorder(bb_succ, order); } order.push(bb); } /** Backward data flow analysis framework. To use, customize the abstract * states, flow functions, and merge functions. * @constructor * @param cfg CFG to analyze. * @param trace Level of debug tracing. 0 is no tracing. 1 is basic state * tracing. 2 and 3 are increasingly verbose. */ function BackwardAnalysis(cfg, trace) { this.cfg = cfg; this.trace = trace; } /** Run the analysis: initialize states and iterate to a fixed point. */ BackwardAnalysis.prototype.run = function() { if (this.trace) print("analysis starting"); // Initialize traversal order let order = postorder(this.cfg); // Start with everything ready so each block gets analyzed at least once. for each (let bb in order) bb.ready = true; let readyCount = order.length; let next = 0; // Fixed point computation this.initStates(order, order[next]); while (readyCount) { // Get next node needing to be processed let index = next; let bb = order[index]; while (!bb.ready) { if (++index == order.length) index = 0; bb = order[index]; } bb.ready = false; readyCount -= 1; if (this.trace) print("update bb: " + bb_label(this.cfg, bb)); // Process bb this.mergeStateOut(bb); if (this.trace) print(" out state: " + this.stateLabel(bb.stateOut)) let changed = this.flowStateIn(bb); if (this.trace) print(" in state: " + this.stateLabel(bb.stateIn) + ' ' + (changed ? '(changed)' : '(same)')); if (changed) { for (let bb_pred in bb_preds(bb)) { if (!bb_pred.ready) { bb_pred.ready = true; readyCount += 1; } } } } }; /** Initialize abstract states for all BBs. */ BackwardAnalysis.prototype.initStates = function(bbs, exit_bb) { // Apparently some bbs don't get in the list sometimes, so let's // be sure to do them all. for (let bb in cfg_bb_iterator(this.cfg)) { //for each (let bb in bbs) { bb.stateIn = this.top(); bb.stateOut = this.top(); } }; /** Set the stateOut to be the merge of all the succ stateIn values. */ BackwardAnalysis.prototype.mergeStateOut = function(bb) { let state = this.top(); for each (let bb_succ in bb_succs(bb)) { this.merge(state, bb_succ.stateIn); } bb.stateOut = state; }; /** Run the flow functions for a basic block, updating stateIn from * stateOut. */ BackwardAnalysis.prototype.flowStateIn = function(bb) { let state = this.copyState(bb.stateOut); for (let isn in bb_isn_iterator_reverse(bb)) { if (this.trace) print(" " + isn_display(isn)); this.flowState(isn, state); if (this.trace) print(" | " + this.stateLabel(state)); } let ans = !equals(bb.stateIn, state); bb.stateIn = state; return ans; } // Functions to be customized per analysis -- defaults are reasonable // except flowState, which must be implemented. /** Customization function. Return the 'top' value to be used in initializing * states. */ BackwardAnalysis.prototype.top = function() { return create_decl_set(); }; /** Customization function. Merge state s2 into s1. That is, do * s1 := s1 \merge s2. */ BackwardAnalysis.prototype.merge = function(s1, s2) { return s1.unionWith(s2); }; /** Customization function. Copy a state. The copy must be 'deep enough' * that modifications to the copy will not affect the original argument. */ BackwardAnalysis.prototype.copyState = function(s) { return s.copy(); }; /** Customization function. Return a string representation of the state * suitable for tracing. */ BackwardAnalysis.prototype.stateLabel = function(s) { return s.toString(); }; /** Customization function. Execute the flow function. I.e., update state * corresponding to abstractly interpreting the given instruction. */ BackwardAnalysis.prototype.flowState = function(isn, state) { throw new Error("abstract method"); }; /** Write a DOT representation of the cfg to the given file. */ function write_dot_graph(cfg, name, filename) { let content = 'digraph ' + name + ' {\n'; content += '\tnode [shape="rect", fontname="Helvetica"];\n' for (let bb in cfg_bb_iterator(cfg)) { //content += '\t' + bb.index + '[label="' + bb_label(cfg, bb) + '"];\n'; let items = [ bb_label(bb), [ isn_display(isn) for (isn in bb_isn_iterator(bb)) ].join('\\n'), 'live: ' + [ expr_display(v) for (v in bb.stateOut.items()) ].join(', ') ]; let label = '{' + items.join('|') + '}'; // Dot requires escaping with record shapes. label = label.replace('>', '\\>') label = label.replace('<', '\\<') content += '\t' + bb.index + '[shape=record,label="' + label + '"];\n' for (let bb_succ in bb_succs(bb)) { content += '\t' + bb.index + ' -> ' + bb_succ.index + ';\n' } } content += '}\n'; write_file(filename, content); } dehydra-89ed48e70997/libs/unstable/cfg_util.js0000644000000000000000000000220011757635752021060 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ function cfg_dfs (visitor, bb, guard) { if (!guard) guard = new InjHashMap() if (guard.has(bb)) return guard.put(bb) visitor.push() for (let isn in bb_isn_iterator(bb)) { visitor.next_isn(isn) } for each (let s in VEC_iterate(bb.succs)) { cfg_dfs(visitor, s.dest, guard) } visitor.pop () } dehydra-89ed48e70997/libs/unstable/dehydra_types.js0000644000000000000000000002627211757635752022147 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include('gcc_print.js'); include('gcc_util.js'); // dehydra_types.c port let TRACE = 0; let typeMap = new Map(); // Return a Dehydra type object corresponding to the given GCC type. function dehydra_convert(type) { if (TRACE) print("dehydra_convert " + type_as_string(type)); let v = typeMap.get(type); if (v) { // For now, Treehydra should never produce incomplete types, so // that code has been removed. return v; } v = {}; typeMap.put(type, v); return dehydra_convert2(type, v); } // Convert the given GCC type object to a Dehydra type by attaching // attributes to 'obj'. // Users should call dehydra_convert. function dehydra_convert2(type, obj) { if (TRACE) print("dehydra_convert2 " + type_as_string(type)); let next_type; let type_decl = TYPE_NAME(type); if (type_decl != undefined) { let original_type = DECL_ORIGINAL_TYPE (type_decl); if (original_type) { obj.name = decl_as_string(type_decl); obj.typedef = dehydra_convert(original_type); dehydra_setLoc(obj, type_decl); attach_attributes(DECL_ATTRIBUTES(type_decl, obj)); return obj; } } if (TYPE_READONLY(type)) obj.isConst = true; if (TYPE_VOLATILE(type)) obj.isVolatile = true; if (TYPE_RESTRICT(type)) obj.restrict = true; switch (TREE_CODE (type)) { case POINTER_TYPE: case OFFSET_TYPE: obj.isPointer = true; next_type = TREE_TYPE(type); break; case REFERENCE_TYPE: obj.isReference = true; next_type = TREE_TYPE(type); break; case RECORD_TYPE: case UNION_TYPE: case ENUMERAL_TYPE: obj.kind = class_key_or_enum_as_string(type); // inlined dehydra_attachClassName obj.name = isAnonymousStruct(type) ? undefined : dehydra_typeString(type); if (!COMPLETE_TYPE_P (type)) { obj.isIncomplete = true; } else if (TREE_CODE (type) == ENUMERAL_TYPE) { dehydra_attachEnumStuff(obj, type); } else { dehydra_attachClassStuff(obj, type); } dehydra_attachTemplateStuff(obj, type); dehydra_setLoc(obj, type); break; case VOID_TYPE: case BOOLEAN_TYPE: case INTEGER_TYPE: case REAL_TYPE: case FIXED_POINT_TYPE: if (!type_decl) { obj.bitfieldBits = TYPE_PRECISION(type); type_decl = TYPE_NAME(type); } obj.name = type_decl ? IDENTIFIER_POINTER(DECL_NAME(type_decl)) : type_as_string(type); break; case COMPLEX_TYPE: case VECTOR_TYPE: /* maybe should add an isTemplateParam? */ case TEMPLATE_TYPE_PARM: case TYPENAME_TYPE: case TYPE_ARGUMENT_PACK: case TYPE_PACK_EXPANSION: obj.name = dehydra_typeString(type); break; case FUNCTION_TYPE: case METHOD_TYPE: dehydra_convertAttachFunctionType (obj, type); break; case ARRAY_TYPE: obj.isArray = true; if (TYPE_DOMAIN (type)) { let dtype = TYPE_DOMAIN (type); let min_t = TYPE_MIN_VALUE (dtype); let max_t = TYPE_MAX_VALUE (dtype); // This logic differs from Dehydra, which simple calls expr_as_string // on max_t. But this version is better: for int[8], this code gives // 8, while Dehydra gives "7u". if (TREE_CODE(min_t) == INTEGER_CST && TREE_CODE(max_t) == INTEGER_CST) { let min_i = TREE_INT_CST_LOW(min_t); let max_i = TREE_INT_CST_LOW(max_t); obj.size = max_i - min_i + 1; } } next_type = TREE_TYPE(type); break; default: throw new Error("Unhandled: " + TREE_CODE(type)); } if (next_type) { obj.type = dehydra_convert(next_type); } return obj; } function isAnonymousStruct(t) { let name = TYPE_NAME(t); if (name) name = DECL_NAME(name); return !name || ANON_AGGRNAME_P(name); } function dehydra_attachTypeAttributes(obj, type) { let attrs = [] let decl_template_info = TYPE_TEMPLATE_INFO (type); if (decl_template_info) { let template_decl = TREE_PURPOSE (decl_template_info); let type = TREE_TYPE (template_decl); attrs = attrs.concat(translate_attributes(TYPE_ATTRIBUTES(type))); } attrs = attrs.concat(translate_attributes(TYPE_ATTRIBUTES(type))); if (attrs.length) obj.attributes = attrs; } function dehydra_attachEnumStuff(objClass, enum_type) { objClass.members = [{'name': IDENTIFIER_POINTER(TREE_PURPOSE(tv)), 'value': TREE_INT_CST_LOW(TREE_VALUE(tv))} for (tv in flatten_chain(TYPE_VALUES(enum_type)))]; } /* Note that this handles class|struct|union nodes */ function dehydra_attachClassStuff(objClass, record_type) { if (TRACE) print("dehydra_attachClassStuff " + type_as_string(record_type)); let binfo = TYPE_BINFO(record_type); if (binfo) { let bases = [ dehydra_convert(BINFO_TYPE(base_binfo)) for each (base_binfo in VEC_iterate(BINFO_BASE_BINFOS(binfo))) ]; if (bases.length) objClass.bases = bases; } objClass.members = []; /* Output all the method declarations in the class. */ for (let func = TYPE_METHODS (record_type) ; func ; func = TREE_CHAIN (func)) { if (DECL_ARTIFICIAL(func)) continue; /* Don't output the cloned functions. */ if (DECL_CLONED_FUNCTION_P (func)) continue; if (TREE_CODE(func) == TEMPLATE_DECL) continue; dehydra_addVar (func, objClass.members); } for (let field = TYPE_FIELDS (record_type); field ; field = TREE_CHAIN (field)) { if (DECL_ARTIFICIAL(field) && !DECL_IMPLICIT_TYPEDEF_P(field)) continue; // ignore typedef of self field // my theory is that the guard above takes care of this one too if (TREE_CODE (field) == TYPE_DECL && TREE_TYPE (field) == record_type) continue; if (TREE_CODE (field) != FIELD_DECL) continue; dehydra_addVar (field, objClass.members); } dehydra_attachTypeAttributes (objClass, record_type); objClass.size_of = TREE_INT_CST_LOW(TYPE_SIZE_UNIT(record_type)); } function dehydra_attachTemplateStuff(parent, type) { if (TRACE) print("dehydra_attachTemplateStuff " + type_as_string(type)); let type_name = TYPE_NAME (type); let decl_artificial = type_name ? DECL_ARTIFICIAL (type_name) : false; if (!(decl_artificial && TREE_CODE (type) != ENUMERAL_TYPE && TYPE_LANG_SPECIFIC (type) && CLASSTYPE_TEMPLATE_INFO (type) && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (type)) != TEMPLATE_DECL || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type))))) { return; } let tpl = CLASSTYPE_TI_TEMPLATE (type); if (!tpl) return; let obj = parent.template = {}; while (DECL_TEMPLATE_INFO (tpl)) tpl = DECL_TI_TEMPLATE (tpl); obj.name = IDENTIFIER_POINTER(DECL_NAME(tpl)); let info = TYPE_TEMPLATE_INFO (type); let args = info ? TI_ARGS (info) : undefined; if (!args) return; if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args)) args = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1); obj.arguments = [ convert_template_arg(arg) for (arg in tree_vec_iterate(args))]; } /** Iterator over a TREE_VEC. Note that this is entirely different from * the vector iterated over by VEC_iterate. */ function tree_vec_iterate(t) { let len = TREE_VEC_LENGTH(t); for (let i = 0; i < len; ++i) { yield TREE_VEC_ELT(t, i); } } function convert_template_arg(arg) { if (TYPE_P(arg)) return dehydra_convert(arg); if (TREE_CODE(arg) == INTEGER_CST) return TREE_INT_CST_LOW(arg); warning("Couldn't convert template parameter with tree code " + TREE_CODE(arg)); return "??"; } function dehydra_convertAttachFunctionType(obj, type) { if (TRACE) print("dehydra_convertAttachFunctionType " + type_as_string(type)); let arg_type = TYPE_ARG_TYPES(type); /* Skip "this" argument. */ // Original dehydra -- this shouldn't work. //if (DECL_NONSTATIC_MEMBER_FUNCTION_P (type)) if (TREE_CODE(type) == METHOD_TYPE) { obj.methodOf = dehydra_convert(TREE_TYPE(TREE_VALUE(arg_type))); arg_type = TREE_CHAIN (arg_type); } /* return type */ obj.type = dehydra_convert(TREE_TYPE(type)); let params = obj.parameters = []; while (arg_type && TREE_CODE(TREE_VALUE(arg_type)) != VOID_TYPE) { params.push(dehydra_convert(TREE_VALUE(arg_type))); arg_type = TREE_CHAIN (arg_type); } } function dehydra_addVar(v, parentArray) { if (!parentArray) throw new Error("missing parentArray"); let obj = {}; parentArray.push(obj); if (!v) return obj; if (TRACE) print("dehydra_addVar " + decl_as_string(v)); if (DECL_P(v)) { /* Common case */ obj.name = decl_as_string(v); let typ = TREE_TYPE (v); if (TREE_CODE (v) == FUNCTION_DECL) { obj.isFunction = true; if (DECL_CONSTRUCTOR_P (v)) obj.isConstructor = true; if (TREE_CODE (typ) == METHOD_TYPE || DECL_CONSTRUCTOR_P (v)) { obj.methodOf = dehydra_convert(DECL_CONTEXT(v)); } if (DECL_PURE_VIRTUAL_P (v)) obj.isVirtual = "pure"; else if (DECL_VIRTUAL_P (v)) obj.isVirtual = true; } obj.type = dehydra_convert(typ); attach_attributes(DECL_ATTRIBUTES(v), obj); if (TREE_STATIC (v)) obj.isStatic = true; } else if (TREE_CODE(v) == CONSTRUCTOR) { /* Special case for this node type */ let type = TREE_TYPE(v); obj.name = type_as_string(type); obj.isConstructor = true; obj.methodOf = dehydra_convert(type); } else { /* Invalid argument tree code */ throw new Error("invalid arg code " + TREE_CODE(v)); } dehydra_setLoc(obj, v); return obj; } function attach_attributes(a, typeobj) { if (a) { typeobj.attributes = translate_attributes(a); } } function dehydra_setLoc(obj, t) { let loc = location_of(t); if (!loc) return; obj.loc = loc_as_string(loc); } function dehydra_typeString(t) { let ans = type_string(t); if (ans.substr(0, 6) == "const ") ans = ans.substr(6); return ans; } // This is a rough port of the GCC function meant for the use of this // module only. function type_as_string(t) { return type_string(t); } // This is a rough port of the GCC function meant for the use of this // module only. function decl_as_string(t) { let ans = decl_name(t); if (TREE_CODE(t) == FUNCTION_DECL) { let type = TREE_TYPE(t); let args = TYPE_ARG_TYPES(type); if (TREE_CODE(type) == METHOD_TYPE) { // skip this args = TREE_CHAIN(args); } // The 'if' checks for void_list_node. This isn't precisely what // GCC checks but should be ok -- it's not like void is a real arg type. let params = [ type_as_string(TREE_VALUE(pt)) for (pt in flatten_chain(args)) if (TREE_CODE(TREE_VALUE(pt)) != VOID_TYPE) ]; ans += '(' + params.join(',') + ')'; } return ans; } dehydra-89ed48e70997/libs/unstable/esp.js0000644000000000000000000005126711757635752020074 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include('unstable/liveness.js'); /* ESP Program Analysis Library */ /** Namespace */ let ESP = function() { /** Map/Set label for ESP states. */ let ps_get_label = function(pskey) { return pskey; }; /** Map/Set key for ESP states. */ let ps_get_key = function(pskey) { return pskey; }; /** Full ESP state. This is a collection of substates, representing * their union. */ let State = function(factory, substate) { // Factory must provide: // .psvbls array of property state vars // .psvblset set of property state vars if (factory == undefined) throw new Error("undefined factory"); this.factory = factory; // Property State -> Substate this.substates = MapFactory.create_map(function (x, y) x == y, function (x) x, ps_get_key, ps_get_label); if (substate != undefined) { this.add(substate); } }; State.prototype.copy = function() { let ans = new State(this.factory); // We need to copy the substate objects as well to avoid sharing. for (let [k, v] in this.substates.getItems()) { ans.substates.put(k, v.copy()); } return ans; }; State.prototype.equals = function(ss) { return this.substates.equals(ss.substates); } State.prototype.list = function() { if (this.substates.isEmpty()) { print(" | Not Reached"); } else { for (let [ps, ss] in this.substates.getItems()) { print(" | " + ss); } } }; /** Add one substate to the current state. */ State.prototype.add = function(ss) { let pskey = ss.pskey(); let cur = this.substates.get(pskey); if (cur) { cur.mergeWith(ss); } else { // TODO This copy *may* not be needed this.substates.put(pskey, ss.copy()); } }; /** Apply an update function to the state. * * This is the primary method used to transform ESP states. Several * other methods wrap this one. The idea is that it's easy to write * a transformation function that transforms a substate (i.e., a * simple mapping of vbls to abstract values). update() will apply * one of those tranforms to the entire state. It is essentially a * map-reduce operation that transforms each substate, then collects * them into a new ESP state. * * The transformer function gets one argument, an ESP substate. The * return value is an array of states, so the transformer can not * only change a value, it can also drop or split substates. * * The transformer is allowed to update a state in place, but if it * returns more than one state, it must make copies of all but one. */ State.prototype.update = function(func) { if (this.substates.isEmpty()) return; let old_substates = this.substates; this.substates = this.substates.create() for (let substate in old_substates.getValues()) { for each (let ns in func(substate)) { this.add(ns); } } }; State.prototype.yieldPreconditions = function (variable) { for each (let substate in this.substates.getValues()) { ret = substate.get(variable) if (ret) yield [ret, substate.getBlame(variable)] } }; function meet (v1, v2, next_meet) { if (v1 === v2) return v1 if (v1 === ESP.TOP) return v2 if (v2 === ESP.TOP) return v1 return next_meet(v1, v2) } function join(v1, v2, next_join) { if (v1 === v2) return v1; if (v1 === ESP.TOP) return v1; if (v2 === ESP.TOP) return v2; return next_join(v1, v2); } /** Return an abstract value, the union of all possible values of vbl * here. */ State.prototype.get = function (vbl) { let first = true; let v; for (let ss in this.substates.getValues()) { // We try to avoid exposing users directly to any NOT_REACHED values // so we avoid calling join on the first element. if (first) { first = false; v = ss.get(vbl); } else { v = join(v, ss.get(vbl), this.factory.join); } } // Here we don't have a choice: the empty set would have no value. return first ? ESP.NOT_REACHED : v; } /** Apply a filter to the state, which keeps substates only where * vbl == val. More formally, set the state to be the conjunction * of itself and the given condition vbl == val. */ State.prototype.filter = function(vbl, val, blame) { let factory = this.factory; this.update(function(ss) { let cur_val = ss.get(vbl); let new_val = meet(cur_val, val, factory.meet); if (new_val == ESP.NOT_REACHED) return []; if (new_val == cur_val) return [ ss ]; ss.assignValue(vbl, val, blame); return [ss]; }); }; /** Update the state by assigning the given abstract value to * the given variable. */ State.prototype.assignValue = function(vbl, value, blame) { if (value == ESP.TOP) { this.remove(vbl, blame); } else { // TODO use lattice? this.update(function(ss) { ss.assignValue(vbl, value, blame); return [ss]; }); } }; /** Update the state to reflect the assignment lhs := rhs, where * both are variables. */ State.prototype.assign = function(lhs, rhs, blame) { this.assignMapped(lhs, function (ss) ss.get(rhs), blame); }; /** Update the state to reflect that we don't know the value. */ State.prototype.remove = function(vbl, blame) { this.assignMapped(vbl, function(ss) undefined, blame); } /** lhs := func(substate) in all states, where func is a JS fn. */ State.prototype.assignMapped = function(lhs, func, blame) { let factory = this.factory; this.update(function (ss) { let rhs = func(ss); if (rhs == undefined || rhs == factory.TOP) { if (factory.psvblset.has(lhs)) { let ans = []; for each (let v in factory.split(lhs, factory.TOP)) { let s = ss.copy(); s.assignValue(lhs, v, blame); ans.push(s); } return ans; } else { ss.remove(lhs); return [ss]; } } ss.assignValue(lhs, rhs, blame); return [ss]; }); }; /** Set the abstract value of all variables not in the given set * to TOP. This effectively demotes these variables to execution * variables, potentially decreasing the size of the state. */ State.prototype.keepOnly = function(var_set) { let factory = this.factory; this.update(function(ss) { let keys = []; for (let v in ss.map.getKeys()) { keys.push(v); } for each (let v in keys) { if (ss.get(v) != factory.TOP && !var_set.has(v)) { ss.remove(v); } } return [ss]; }); } /** Merge the given state into this one. */ State.prototype.mergeWith = function(state) { for (let ss in state.substates.getValues()) { this.add(ss); } }; /** ESP substate. This is a mapping of variables to abstract values. * The substate also tracks a 'blame' for each variable, which is * a statement that caused it to have its current value. */ let Substate = function(factory, map, blames) { this.factory = factory; this.map = map; this.blames = blames; }; Substate.prototype.copy = function() { return new Substate(this.factory, this.map.copy(), this.blames.copy()); }; Substate.prototype.equals = function(ss) { return this.map.equals(ss.map); }; function toShortString(latticeValue) { if (latticeValue === ESP.TOP) return "T" if (latticeValue.toShortString) return latticeValue.toShortString() return latticeValue.toString() } /** Return a string value that uniquely represents the property state * of this substate. */ Substate.prototype.pskey = function() { return Array.join([ toShortString(this.map.get(vbl)) for each (vbl in this.factory.psvbls) ], ''); }; Substate.prototype.toString = function() { return this.pskey() + ' ' + Array.join([ expr_display(vbl) + ':' + toShortString(this.map.get(vbl)) for (vbl in this.map.getKeys()) ], ', '); }; /** Return the abstract value of the given variable. */ Substate.prototype.get = function(vbl) { return this.map.get(vbl); } /** Return the instruction to blame for this variable having the value * it currently has. */ Substate.prototype.getBlame = function(vbl) { return this.blames.get(vbl); } // Substate mutators must only be called through State update functions. /** Not for public use. See source code. */ Substate.prototype.assignValue = function(vbl, value, blame) { this.map.put(vbl, value); this.blames.put(vbl, blame); }; /** Not for public use. See source code. */ Substate.prototype.remove = function(vbl, value, blame) { if (this.factory.psvblset.has(vbl)) { this.map.put(vbl, this.factory.TOP); this.blames.put(vbl, blame); } else { this.map.remove(vbl); } }; Substate.prototype.getVariables = function () { return this.map.getKeys(); } /** Not for public use. See source code. */ Substate.prototype.mergeWith = function(ss) { // We have to be very careful -- we keep a value only if present // on both sides and equal. let new_map = this.map.create(); for (let [k,v] in this.map.getItems()) { if (this.factory.psvblset.has(k)) { if (v != ss.map.get(k)) throw new Error("assert"); } if (v == ss.map.get(k)) { new_map.put(k, v); } } this.map = new_map; }; /** ESP program analysis solver framework. * Based on: * ESP: path-sensitive program verification in polynomial time. * Manuvir Das, Sorin Lerner, and Mark Seigle. * http://portal.acm.org/citation.cfm?id=512538 * * @param cfg CFG to analyze * @param psvbls List of PropVarSpec objects describing state variables * @param bottom Abstract value representing TOP state * for a variable, i.e., any value * @param trace Debug tracing level: 0, 1, 2, or 3. */ let Analysis = function(cfg, psvbls, meet, trace) { // if used as prototype don't do anything if (!cfg) return if (meet == undefined) { meet = this.default_meet; // Can't issue a warning here because instances used as prototypes // would print warnings } this.cfg = cfg; this.trace = trace; this.meet = meet; this.psvbls = []; // We do live variable analysis so that we can throw away // dead variables and make ESP faster. { let trace = 0; let b = new LivenessAnalysis(cfg, trace); b.run(); for (let bb in cfg_bb_iterator(cfg)) { bb.keepVars = bb.stateIn; } } // Link switches so ESP knows switch conditions. for each (let finally_tmp in link_switches(cfg)) { // Add finally temps so we can decode GCC destructor usages. this.psvbls.push(finally_tmp) } this.startValues = create_decl_map(); for each (let v in psvbls) { this.psvbls.push(v.vbl); this.startValues.put(v.vbl, v.initial_val) if (!v.keep) continue for (let bb in cfg_bb_iterator(cfg)) { bb.keepVars.add(v.vbl); } } this.psvblset = create_decl_set(this.psvbls); // Resource limits -- Set these properties directly to control halting this.time_limit = undefined; // wall clock time in ms this.pass_limit = undefined; // number of passes per BB if (trace) { print("PS vars"); for each (let v in this.psvbls) { print(" " + expr_display(v)); } } }; /** Run the solver to a fixed point. */ Analysis.prototype.run = function() { let t0 = new Date(); if (this.trace) print("analysis starting"); if (this.meet == this.default_meet) { warning("ESP solver warning: using unsound default meet() function.\n" + "Some code paths may be wrongly excluded from analysis."); } // Make us valid as a factory for States, while still allowing us // to be a prototype in the usual idiom. if (this.psvbls == undefined) throw new Error("Invalid psvbls"); // Initialize traversal order let order = postorder(this.cfg); order.reverse(); // Start with everything ready so each block gets analyzed at least once. for each (let bb in order) { bb.ready = true; bb.pass_count = 0; // number of passes made over this BB } let readyCount = order.length; let next = 0; // Fixed point computation this.initStates(order, order[next]); this.cfg.x_entry_block_ptr.stateIn = new State(this, new Substate(this, this.startValues, create_decl_map()));; if (this.trace) { print("initial state:"); this.cfg.x_entry_block_ptr.stateIn.list(); } while (readyCount) { let dt = new Date() - t0; if (dt > this.time_limit) throw new Error("ESP halting: time limit exceeded (" + this.time_limit + " ms)"); // Get next node needing to be processed let index = next; let bb = order[index]; while (!bb.ready) { if (++index == order.length) index = 0; bb = order[index]; } bb.ready = false; readyCount -= 1; if (this.trace) { let loc for each (let isn in bb_isn_iterator(bb)) { loc = location_of(isn) if (loc) break; } print("update bb: " + bb_label(this.cfg, bb), loc); } // Process bb if (bb != this.cfg.x_entry_block_ptr) { if (!this.mergeStateIn(bb) && bb.pass_count > 0) { if (this.trace) print(" in state unchanged"); continue; } } ++bb.pass_count; if (bb.pass_count > this.pass_limit) throw new Error("ESP halting: BB pass limit exceeded (" + bb_label(bb) + " on pass " + bb.pass_count + ")"); if (this.trace) { print(" in state:"); bb.stateIn.list(); } let changed = this.flowStateOut(bb); if (this.trace) { print(" out state:" + (changed ? " (changed)" : " (same)")); bb.stateOut.list(); } if (changed) { // Update states on outedges, applying facts from conditional // branches if applicable for each (let e in bb_succ_edges(bb)) { let bb_succ = e.dest; if (this.trace >= 2) print(" succ " + bb_label(bb_succ)); if (e.flags & EDGE_TRUE_VALUE || e.flags & EDGE_FALSE_VALUE) { // Outgoing edges from an if let truth = (e.flags & EDGE_TRUE_VALUE) != 0; let edge_state = bb.stateOut.copy(); let cond = bb_isn_last(bb); if (this.trace) { print(" branch " + truth + ": " + gs_display(cond)); } this.flowStateCond(cond, truth, edge_state); e.state = edge_state; if (this.trace) { print (" edge state:"); e.state.list(); } } else if (e.hasOwnProperty('case_val')) { // Outgoing edges from a switch let truth = e.case_val; let edge_state = bb.stateOut.copy(); let cond = bb_isn_last(bb); if (this.trace) { print(" branch " + truth + ": " + gs_display(cond)); } this.flowStateCond(cond, truth, edge_state); e.state = edge_state; if (this.trace) { print (" edge state:"); e.state.list(); } } else { // Need to take a copy here as well because we're going to // keep live vars only, which can vary on succs. e.state = bb.stateOut.copy(); } this.updateEdgeState(e); if (!bb_succ.ready) { bb_succ.ready = true; readyCount += 1; } } } } }; /** Initialize abstract states for all BBs and edges. */ Analysis.prototype.initStates = function(bbs, exit_bb) { // Apparently some bbs don't get in the list sometimes, so let's // be sure to do them all. for (let bb in cfg_bb_iterator(this.cfg)) { bb.stateIn = this.top(); bb.stateOut = this.top(); for each (let e in bb_succ_edges(bb)) { e.state = this.top(); } } }; /** Set the stateIn to be the merge of all the pred stateOut values. * Return true if the result is different from the original stateIn. */ Analysis.prototype.mergeStateIn = function(bb) { let state = this.top(); for each (let e in bb_pred_edges(bb)) { if (this.trace >= 3) { print("merge " + edge_string(e, this.cfg)); e.state.list(); } this.merge(state, e.state); } let ans = !bb.stateIn.equals(state); bb.stateIn = state; return ans; }; /** Run the flow functions for a basic block, updating stateOut from * stateIn. */ Analysis.prototype.flowStateOut = function(bb) { let state = this.copyState(bb.stateIn); for (let isn in bb_isn_iterator(bb)) { if (this.trace) { if (this.trace >= 2) { print(" loc: " + location_of(isn)); } print(" " + gs_display(isn)); } this.flowState(isn, state); if (this.trace) { state.list(); } } if (this.trace) { print(" old state:"); bb.stateOut.list(); } let ans = !bb.stateOut.equals(state); bb.stateOut = state; return ans; } /** Return the 'top' state representing no behavior. This should not * need to be customized. */ Analysis.prototype.top = function() { return new State(this); }; /** Merge state s2 into state s1. This should not need to be customized. */ Analysis.prototype.merge = function(s1, s2) { return s1.mergeWith(s2); }; /** Copy a state. This should not need to be customized. */ Analysis.prototype.copyState = function(s) { return s.copy(); }; /** Return a string representation of the state for tracing. This * should not need to be customized. */ Analysis.prototype.stateLabel = function(s) { return s.toString(); }; /** Customization function. Update 'state' by abstractly interpreting * instruction 'isn' on the state. */ Analysis.prototype.flowState = function(isn, state) { throw new Error("abstract method"); }; /** Customization function. Update 'state' according to following the * given conditional branch. * @param isn Branch instruction: COND_EXPR or SWITCH_EXPR. * @param truth Value of conditional expression of 'isn' for this branch. * @param state State on arrival at branch. This function should update * the state, although doing nothing is valid, just less * precise. * @see State.predicate */ Analysis.prototype.flowStateCond = function(isn, truth, state) { throw new Error("abstract method"); }; /** Customization function. Update the state on the given edge. This * is purely optional, override it to disable. * This can be used for dropping state on variables no longer of interest. * @param edge CFG edge, with src and dest BB properties. There * is also the .state property. */ Analysis.prototype.updateEdgeState = function(edge) { edge.state.keepOnly(edge.dest.keepVars); }; /** Default meet operator. Note that this version is unsound. If used, * a warning will be issued. Return value of undefined indicates * empty set state (TODO, s/b lattice member?). */ Analysis.default_meet = function(v1, v2) { return ESP.NOT_REACHED; }; /** Split a property state abstract value. The return value should * be a list of abstract values whose join (union) is equal to the * input. This is used by assignMapped to split values like TOP for * later more precise handling. Generally, the right thing to do * is to return the list of specific abstract values possible for * the given property variable. The default implementation is sound.*/ Analysis.prototype.split = function(vbl, v) { return [ v ]; } /** * vbl: variable * keep: don't drop variables due to liveness * initial_val: initial lattice value..defaults to undefined which is ESP.TOP */ PropVarSpec = function (vbl,keep,initial_val) { this.vbl = vbl this.keep = keep this.initial_val = initial_val } return {Analysis: Analysis, PropVarSpec: PropVarSpec, TOP:undefined, NOT_REACHED:{}}; }(); dehydra-89ed48e70997/libs/unstable/getopt.js0000644000000000000000000000245511757635752020602 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** * Take an array of arguments (or this.arguments if nothing is passed) * and split it into options and arguments: --option=value arg... * @returns [options, arguments] */ function getopt(o) { if (o === undefined) o = this.options.split(/ +/); let opts = {}; let args = []; let optionFinder = /^--([a-z_-]+)=(.*)$/i; for each (arg in o) { let m = optionFinder(arg); if (m) opts[m[1]] = m[2]; else args.push(arg); } return [opts, args]; } dehydra-89ed48e70997/libs/unstable/lazy_types.js0000644000000000000000000004263011757635752021502 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ include('gcc_print.js'); include('gcc_util.js'); include('unstable/BigInt.js'); // dehydra_types.c port let TRACE = 0; let typeMap = new Map(); function LazyType(type) { this._type = type; } LazyType.prototype.__defineGetter__('loc', function type_loc() { return location_of(this._type); }); LazyType.prototype.__defineGetter__('isConst', function type_const() { return TYPE_READONLY(this._type) ? true : undefined; }); LazyType.prototype.__defineGetter__('isVolatile', function type_volatile() { return TYPE_VOLATILE(this._type) ? true : undefined; }); LazyType.prototype.__defineGetter__('restrict', function type_restrict() { return TYPE_RESTRICT(this._type) ? true : undefined; }); LazyType.prototype.__defineGetter__('attributes', function type_atts() { switch (TREE_CODE(this._type)) { case TYPENAME_TYPE: case TEMPLATE_TYPE_PARM: case TYPEOF_TYPE: case DECLTYPE_TYPE: // types for which TYPE_P does not hold. argh. break; default: if (!TYPE_P(this._type)) throw new Error("code " + TREE_CODE(this._type) + " is not a type!"); } return translate_attributes(TYPE_ATTRIBUTES(this._type)); }); LazyType.prototype.__defineGetter__('variantOf', function type_variant() { let variant = TYPE_MAIN_VARIANT(this._type); if (variant === this._type) return undefined; return dehydra_convert(variant); }); LazyType.prototype.__defineGetter__('typedef', function type_typedef() { let type_decl = TYPE_NAME (this._type); if (type_decl && TREE_CODE(type_decl) == TYPE_DECL) { let original_type = DECL_ORIGINAL_TYPE(type_decl); if (original_type) return dehydra_convert(original_type); } return undefined; }); LazyType.prototype.__defineGetter__('name', function type_name() { return type_string(this._type); }); LazyType.prototype.getQuals = function type_getquals() { let q = []; // if (this.isConst) // q.push('const '); if (this.isVolatile) q.push('volatile '); if (this.restrict) q.push('restrict '); return q.join(''); } LazyType.prototype.toString = function() { return this.name; }; /* A type which has a .type subtype accessible via TREE_TYPE */ function LazySubtype(type) { } LazySubtype.prototype = new LazyType(); LazySubtype.prototype.__defineGetter__('type', function subtype_type() { return dehydra_convert(TREE_TYPE(this._type)); }); function LazyPointer(type) { this._type = type; } LazyPointer.prototype = new LazySubtype(); LazyPointer.prototype.isPointer = true; LazyPointer.prototype.__defineGetter__('precision', function pointer_prec() { return TYPE_PRECISION(this._type); }); LazyPointer.prototype.toString = function() { return this.type + "*"; }; function LazyReference(type) { this._type = type; } LazyReference.prototype = new LazySubtype(); LazyReference.prototype.isReference = true; LazyReference.prototype.__defineGetter__('precision', function ref_prec() { return TYPE_PRECISION(this._type); }); LazyReference.prototype.toString = function () { return this.type + "&"; }; function LazyRecord(type) { this._type = type; this.kind = class_key_or_enum_as_string(type); if (!COMPLETE_TYPE_P(type)) this.isIncomplete = true; } LazyRecord.prototype = new LazyType(); LazyRecord.prototype.__defineGetter__('bases', function record_bases() { if (this.isIncomplete) throw Error("Can't get bases of incomplete type " + this.name); let binfo = TYPE_BINFO(this._type); // Also: expose .isVirtual on bases let accesses = VEC_iterate(BINFO_BASE_ACCESSES(binfo)); let bases = []; let i = 0; for each (base_binfo in VEC_iterate(BINFO_BASE_BINFOS(binfo))) { bases.push({'access': IDENTIFIER_POINTER(accesses[i++]), 'type': dehydra_convert(BINFO_TYPE(base_binfo)), 'isVirtual': BINFO_VIRTUAL_P(base_binfo) ? true : undefined, 'toString': function() { return this.access + ' ' + this.type; }}); } return bases; }); LazyRecord.prototype.__defineGetter__('members', function record_members() { let members = []; for (let func in flatten_chain(TYPE_METHODS(this._type))) { if (DECL_ARTIFICIAL(func)) continue; if (TREE_CODE(func) == TEMPLATE_DECL) continue; members.push(dehydra_convert(func)); } for (let field in flatten_chain(TYPE_FIELDS(this._type))) { if (DECL_ARTIFICIAL(field) && !DECL_IMPLICIT_TYPEDEF_P(field)) continue; // ignore typedef of self field // my theory is that the guard above takes care of this one too if (TREE_CODE (field) == TYPE_DECL && TREE_TYPE (field) == this._type) continue; if (TREE_CODE (field) != FIELD_DECL) continue; members.push(dehydra_convert(field)); } return members; }); LazyRecord.prototype.__defineGetter__('size_of', function record_size() { return TREE_INT_CST_LOW(TYPE_SIZE_UNIT(this._type)); }); LazyRecord.prototype.__defineGetter__('attributes', function record_atts() { let attrs = []; let decl_template_info = TYPE_TEMPLATE_INFO (this._type); if (decl_template_info) { let template_decl = TI_TEMPLATE (decl_template_info); let type = TREE_TYPE (template_decl); attrs = attrs.concat(translate_attributes(TYPE_ATTRIBUTES(type))); } attrs = attrs.concat(translate_attributes(TYPE_ATTRIBUTES(this._type))); return attrs; }); LazyRecord.prototype.__defineGetter__('template', function record_args() { let type_name = TYPE_NAME (this._type); let decl_artificial = type_name ? DECL_ARTIFICIAL (type_name) : false; if (!(decl_artificial && TYPE_LANG_SPECIFIC (this._type) && CLASSTYPE_TEMPLATE_INFO (this._type) && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (this._type)) != TEMPLATE_DECL || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (this._type))))) return undefined; let tpl = CLASSTYPE_TI_TEMPLATE (this._type); if (!tpl) return undefined; let template = {}; while (DECL_TEMPLATE_INFO(tpl)) tpl = DECL_TI_TEMPLATE(tpl); template.name = IDENTIFIER_POINTER(DECL_NAME(tpl)); let info = TYPE_TEMPLATE_INFO(this._type); let args; if (info) args = TI_ARGS(info); if (args) { if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS(args)) args = TREE_VEC_ELT(args, TREE_VEC_LENGTH(args) - 1); template.arguments = [convert_template_arg(arg) for (arg in tree_vec_iterate(args))]; } return template; }); LazyRecord.prototype.toString = function() { return this.name; }; function LazyEnum(type) { this._type = type; if (!COMPLETE_TYPE_P(type)) this.isIncomplete = true; } LazyEnum.prototype = new LazyType(); LazyEnum.prototype.kind = 'enum'; LazyEnum.prototype.__defineGetter__('members', function enum_members() { let members = []; for (let tv in flatten_chain(TYPE_VALUES(this._type))) { let obj = { "name" : IDENTIFIER_POINTER(TREE_PURPOSE(tv)) } let val = TREE_VALUE(tv); if (TREE_CODE(val) == CONST_DECL) val = DECL_INITIAL(val); obj.value = TREE_INT_CST_LOW(val); members.push(obj); } return members; }); LazyEnum.prototype.toString = function() { return "enum " + this.name; }; function LazyNumber(type, type_decl) { this._type = type; if (!type_decl) { type_decl = TYPE_NAME(type); } }; LazyNumber.prototype = new LazyType(); LazyNumber.prototype.__defineGetter__('isUnsigned', function number_unsigned() { return TYPE_UNSIGNED(this._type) ? true : undefined; }); LazyNumber.prototype.__defineGetter__('isSigned', function number_signed() { return TYPE_UNSIGNED(this._type) ? undefined : true; }); LazyNumber.prototype.__defineGetter__('bitfieldBits', function number_bf() { let parent = c_common_type_for_mode(TYPE_MODE(this._type), TYPE_UNSIGNED(this._type)); if (parent === this.type) return undefined; return TYPE_PRECISION(this._type); }); LazyNumber.prototype.__defineGetter__('bitfieldOf', function numbef_bfof() { let parent = c_common_type_for_mode(TYPE_MODE(this._type), TYPE_UNSIGNED(this._type)); if (parent === this._type) return undefined; return dehydra_convert(parent); }); LazyNumber.prototype.__defineGetter__('min', function number_min() { let min = TYPE_MIN_VALUE(this._type); if (!min) return undefined; return dehydra_convert(min); }); LazyNumber.prototype.__defineGetter__('max', function number_max() { let max = TYPE_MAX_VALUE(this._type); if (!max) return undefined; return dehydra_convert(max); }); LazyNumber.prototype.__defineGetter__('precision', function number_prec() { return TYPE_PRECISION(this._type); }); LazyNumber.prototype.toString = function() { return this.name; }; function LazyArray(type) { this._type = type; } LazyArray.prototype = new LazySubtype(); LazyArray.prototype.isArray = true; LazyArray.prototype.__defineGetter__('max', function array_max() { let d = TYPE_DOMAIN(this._type); if (d === undefined) return undefined; let max = TYPE_MAX_VALUE(d); if (max === undefined || TREE_CODE(max) != INTEGER_CST) return undefined; return tree_to_bigint(max); }); LazyArray.prototype.__defineGetter__('variableLength', function array_vlength() { return this.max === undefined; }); LazyArray.prototype.toString = function() { return this.type + " [" + (this.size === undefined ? "" : this.size) + "]"; }; function LazyFunctionType(type) { this._type = type; } LazyFunctionType.prototype = new LazySubtype(); LazyFunctionType.prototype.__defineGetter__('parameters', function fntype_parameters() { return [dehydra_convert(arg_type) for (arg_type in function_type_args(this._type))]; }); LazyFunctionType.prototype.toString = function() { return this.type + " ()(" + this.parameters.join(', ') + ")"; }; const hexMap = []; hexMap[48] = 102; // 0 -> f hexMap[49] = 101; hexMap[50] = 100; hexMap[51] = 99; hexMap[52] = 98; hexMap[53] = 97; hexMap[54] = 57; hexMap[55] = 56; hexMap[56] = 55; hexMap[57] = 54; hexMap[97] = 53; hexMap[98] = 52; hexMap[99] = 51; hexMap[100] = 50; hexMap[101] = 49; hexMap[102] = 48; function bitwiseNot(hexString) { return String.fromCharCode.apply(null, [hexMap[hexString.charCodeAt(i)] for (i in hexString)]); } function tree_to_bigint(t) { switch (TREE_CODE(t)) { case INTEGER_CST: let cst = TREE_INT_CST(t); let v = new BigInt('0x' + cst.high_str + cst.low_str); if (tree_int_cst_sign(t) < 0) { // manually do ~val + 1 arithmetic let nc = cst.high_str.length * 2; function ncrange() { for (let i = 0; i < nc; ++i) yield i; } let maxval = new BigInt('0x' + ['f' for (i in ncrange())].join('')); return bigint_uminus(bigint_plus(bigint_minus(maxval, v), 1)); } return v; default: throw Error("Unhandled number type: " + TREE_CODE(t)); } } function LazyLiteral(type) { this._type = type; this.value = tree_to_bigint(type); } LazyLiteral.prototype.__defineGetter__('type', function lazyliteral_type() { let t = TREE_TYPE(this._type); if (t !== undefined) return dehydra_convert(t); return undefined; }); function LazyDecl(type) { this._type = type; } LazyDecl.prototype.__defineGetter__('loc', function lazydecl_loc() { return location_of(this._type); }); LazyDecl.prototype.__defineGetter__('attributes', function lazydecl_atts() { return translate_attributes(DECL_ATTRIBUTES(this._type)); }); LazyDecl.prototype.__defineGetter__('type', function lazydecl_type() { return dehydra_convert(TREE_TYPE(this._type)); }); LazyDecl.prototype.__defineGetter__('name', function lazydecl_name() { let n = decl_name(this._type); if (this.isFunction) { let plist = this.type.parameters; if (this.memberOf) plist.unshift(); // remove "this" n += "(" + [p.toString() for each (p in plist)].join(", ") + ")"; } return n; }); LazyDecl.prototype.__defineGetter__('shortName', function lazydecl_shortName() { return IDENTIFIER_POINTER(DECL_NAME(this._type)); }); LazyDecl.prototype.__defineGetter__('isFunction', function lazydecl_isfunc() { return TREE_CODE(this._type) == FUNCTION_DECL; }); LazyDecl.prototype.__defineGetter__('isConstructor', function lazydecl_constructor() { if (!this.isFunction) return undefined; if (DECL_CONSTRUCTOR_P(this._type)) return true; return false; }); LazyDecl.prototype.__defineGetter__('isDestructor', function lazydecl_destructor() { if (!this.isFunction) return undefined; return isDestructor(this._type); }); LazyDecl.prototype.__defineGetter__('memberOf', function lazydecl_methodof() { let cx = DECL_CONTEXT(this._type); if (cx && TREE_CODE(cx) == RECORD_TYPE) return dehydra_convert(cx); return undefined; }); LazyDecl.prototype.__defineGetter__('access', function lazydecl_access() { if (TREE_PRIVATE(this._type)) return "private"; else if (TREE_PROTECTED(this._type)) return "protected"; return "public"; }); LazyDecl.prototype.__defineGetter__('isVirtual', function lazydecl_isvirt() { if (!this.isFunction) return undefined; if (DECL_PURE_VIRTUAL_P(this._type)) return "pure"; else if (DECL_VIRTUAL_P(this._type)) return true; else return false; }); LazyDecl.prototype.__defineGetter__('isStatic', function lazydecl_isstatic() { let code = TREE_CODE(this._type); if (code == VAR_DECL && TREE_STATIC(this._type)) return true; if (code == FUNCTION_DECL && !TREE_PUBLIC(this._type)) return true; if (TREE_CODE(TREE_TYPE(this._type)) == FUNCTION_TYPE && this.memberOf) return true; return undefined; }); LazyDecl.prototype.__defineGetter__('isExtern', function lazydecl_isextern() { let code = TREE_CODE(this._type); if (code != VAR_DECL || this.memberOf === undefined || TREE_STATIC(this._type)) return true; return undefined; }); LazyDecl.prototype.__defineGetter__('parameters', function lazydecl_params() { if (TREE_CODE(this._type) != FUNCTION_DECL) return undefined; return [dehydra_convert(t) for (t in flatten_chain(DECL_ARGUMENTS(this._type)))]; }); LazyDecl.prototype.toString = function() { return this.name; } function LazyConstructor(type) { this._type = type; } LazyConstructor.prototype = new LazyType(); LazyConstructor.prototype.__defineGetter__('name', function lazycons_name() { return type_as_string(TREE_TYPE(this._type)); }); LazyConstructor.prototype.isConstructor = true; LazyConstructor.prototype.__defineGetter__('memberOf', function lazycons_methodOf() { return dehydra_convert(TREE_TYPE(this._type)); }); LazyConstructor.prototype.toString = function() { return "LazyConstructor instance"; } // Convert the given GCC type object to a lazy Dehydra type function dehydra_convert(type) { if (TRACE) print("dehydra_convert " + type_as_string(type)); if (TREE_CODE(type) == TYPE_DECL && DECL_ORIGINAL_TYPE(type) !== undefined) type = TREE_TYPE(type); switch (TREE_CODE(type)) { case POINTER_TYPE: case OFFSET_TYPE: return new LazyPointer(type); case REFERENCE_TYPE: return new LazyReference(type); case RECORD_TYPE: case UNION_TYPE: return new LazyRecord(type); case ENUMERAL_TYPE: return new LazyEnum(type); case VOID_TYPE: case BOOLEAN_TYPE: case INTEGER_TYPE: case REAL_TYPE: case FIXED_POINT_TYPE: return new LazyNumber(type); case ARRAY_TYPE: return new LazyArray(type); case COMPLEX_TYPE: case VECTOR_TYPE: /* maybe should add an isTemplateParam? */ case TEMPLATE_TYPE_PARM: case TYPENAME_TYPE: case TYPE_ARGUMENT_PACK: case TYPE_PACK_EXPANSION: case TYPEOF_TYPE: case DECLTYPE_TYPE: return new LazyType(type); case FUNCTION_TYPE: case METHOD_TYPE: return new LazyFunctionType(type); case CONSTRUCTOR: return new LazyConstructor(type); case INTEGER_CST: return new LazyLiteral(type); }; if (DECL_P(type)) return new LazyDecl(type); throw new Error("Unknown type. TREE_CODE: " + TREE_CODE(type)); } function isDestructor(fndecl) { return fndecl.decl_common.lang_specific.decl_flags.destructor_attr; } /** Iterator over a TREE_VEC. Note that this is entirely different from * the vector iterated over by VEC_iterate. */ function tree_vec_iterate(t) { let len = TREE_VEC_LENGTH(t); for (let i = 0; i < len; ++i) { yield TREE_VEC_ELT(t, i); } } function convert_template_arg(arg) { if (TYPE_P(arg)) return dehydra_convert(arg); if (TREE_CODE(arg) == INTEGER_CST) return TREE_INT_CST_LOW(arg); warning("Couldn't convert template parameter with tree code " + TREE_CODE(arg)); return "??"; } // This is a rough port of the GCC function meant for the use of this // module only. function type_as_string(t) { return type_string(t); } dehydra-89ed48e70997/libs/unstable/liveness.js0000644000000000000000000000364111757635752021126 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** Liveness analysis.. */ function LivenessAnalysis() { BackwardAnalysis.apply(this, arguments); } LivenessAnalysis.prototype = new BackwardAnalysis; LivenessAnalysis.prototype.flowState = function(isn, state) { switch (gimple_code(isn)) { case RETURN_EXPR: // 4.3 issue: Need to specially handle embedded trees within RETURN_EXPRs let gms = TREE_OPERAND(isn, 0); if (gms) { // gms is usually a GIMPLE_MODIFY_STMT but can be a RESULT_DECL if (TREE_CODE(gms) == GIMPLE_MODIFY_STMT) { let v = GIMPLE_STMT_OPERAND(gms, 1); state.add(v); } else if (TREE_CODE(gms) == RESULT_DECL) { // TODO figure out what really happens here // Presumably we already saw the assignment to it. } } break; case GIMPLE_RETURN: case GIMPLE_ASSIGN: case GIMPLE_COND: case GIMPLE_SWITCH: case GIMPLE_CALL: for (let e in isn_defs(isn, 'strong')) { if (DECL_P(e)) { state.remove(e); } } for (let e in isn_uses(isn)) { if (DECL_P(e)) { state.add(e); } } break; default: break; } }; dehydra-89ed48e70997/libs/unstable/zero_nonzero.js0000644000000000000000000002047011757635752022026 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Base abstract interpretation for booleans and ints. This is * useful for a lot of basic control flow, especially GCC-generated * conditions and switches. */ /** Our value lattice. The lattice also includes all integer values, * which are encoded directly as integers. */ const Lattice = { ZERO: 0, NONZERO: {toString: function() {return "NONZERO"}, toShortString: function() {return "!0"}} } /** Negate an abstract value as a _condition_. By this, we mean return * the abstract value that represents all concrete values not in v. * We do not mean operational negation, i.e., the effect of applying * a C++ negation operator. */ function negate (v) { switch (v) { case Lattice.ZERO: return Lattice.NONZERO; case Lattice.NONZERO: return Lattice.ZERO; default: // It would be sound to return BOTTOM here, but this case shouldn't // be reached in current code. throw new Error("Tried to negate non-boolean abstract value " + v); } } function Zero_NonZero() { } Zero_NonZero.prototype.flowState = function(isn, state) { switch (gimple_code(isn)) { case GIMPLE_ASSIGN: this.processAssign(isn, state); break; case RETURN_EXPR: // 4.3 embeds assignments in returns let op = isn.operands()[0]; if (op) this.processAssign(op, state); break; case GIMPLE_CALL: this.processCall(isn, state); } } Zero_NonZero.prototype.processCall = function (isn, state) { // these are handled by processAssign (4.3 issue) if (isn.tree_code() == CALL_EXPR) return; for (let arg in gimple_call_arg_iterator(isn)) { if (arg.tree_code() == ADDR_EXPR) { let vbl = arg.operands()[0]; if (DECL_P(vbl)) { state.assignValue(vbl, ESP.TOP, isn); } } } let lhs = gimple_call_lhs(isn); if (!lhs) return; lhs = unwrap_lhs(lhs); let fname = gimple_call_function_name(isn); if (fname == '__builtin_expect') { // Same as an assign from arg 0 to lhs state.assign(lhs, gimple_call_arg(isn, 0), isn); } else { state.assignValue(lhs, ESP.TOP, isn); } } function unwrap_lhs(lhs) { switch (lhs.tree_code()) { case COMPONENT_REF: case INDIRECT_REF: case ARRAY_REF: lhs = TREE_OPERAND(lhs, 0); return unwrap_lhs(lhs); } return lhs; } // State transition for assignments. We'll just handle constants and copies. // For everything else, the result is unknown (i.e., TOP). Zero_NonZero.prototype.processAssign = function(isn, state) { let lhs = gimple_op(isn, 0); let rhs = gimple_op(isn, 1); if (DECL_P(lhs)) { // Unwrap NOP_EXPR, which is semantically a copy. if (TREE_CODE(rhs) == NOP_EXPR) { rhs = rhs.operands()[0]; } if (DECL_P(rhs)) { state.assign(lhs, rhs, isn); return; } switch (TREE_CODE(rhs)) { case INTEGER_CST: // The exact value is required for handling control flow around // finally_tmp variables. We can use it always now that we have // a lattice with meet. let value = TREE_INT_CST_LOW(rhs); state.assignValue(lhs, value, isn); break; case NE_EXPR: { // We only care about gcc-generated x != 0 for conversions and such. let [op1, op2] = rhs.operands(); if (DECL_P(op1) && expr_literal_int(op2) == 0) { state.assign(lhs, op1, isn); } break; } case CALL_EXPR: /* only relevant with GCC 4.3 */ let fname = call_function_name(rhs); if (fname == '__builtin_expect') { // Same as an assign from arg 0 to lhs state.assign(lhs, call_args(rhs)[0], isn); } else { state.assignValue(lhs, ESP.TOP, isn); for (let arg in call_arg_iterator(rhs)) { if (arg.tree_code() == ADDR_EXPR) { let vbl = arg.operands()[0]; if (DECL_P(vbl)) { state.assignValue(vbl, ESP.TOP, isn); } } } } break; case TRUTH_NOT_EXPR: let not_operand = rhs.operands()[0]; if (DECL_P(not_operand)) { state.assignMapped(lhs, function(ss) { switch (ss.get(not_operand)) { case undefined: return undefined; case 0: return 1; default: return 0; } }, isn); } else { state.remove(lhs, isn); } break; // Stuff we don't analyze -- just kill the LHS info case EQ_EXPR: case ADDR_EXPR: case POINTER_PLUS_EXPR: case ARRAY_REF: case COMPONENT_REF: case INDIRECT_REF: case FILTER_EXPR: case EXC_PTR_EXPR: case CONSTRUCTOR: case REAL_CST: case STRING_CST: case CONVERT_EXPR: case TRUTH_XOR_EXPR: case BIT_FIELD_REF: state.remove(lhs); break; default: if (UNARY_CLASS_P(rhs) || BINARY_CLASS_P(rhs) || COMPARISON_CLASS_P(rhs)) { state.remove(lhs); break; } } } } Zero_NonZero.prototype.flowStateCond = function(isn, truth, state) { let code = gimple_code(isn) switch (code) { case GIMPLE_COND: this.flowStateIf(isn, truth, state); break; case GIMPLE_SWITCH: this.flowStateSwitch(isn, truth, state); break; default: throw new Error("flowStateCond: Unhandled gimple:" +code) } } // Apply filter for an if statement. This handles only tests for // things being zero or nonzero. Zero_NonZero.prototype.flowStateIf = function(isn, truth, state) { let comparison = gimple_cond_code(isn); switch (comparison) { case EQ_EXPR: case NE_EXPR: let op1 = condition_operand(isn, 0); let op2 = condition_operand(isn, 1); if (expr_literal_int(op1) != undefined) { [op1,op2] = [op2,op1]; } if (!DECL_P(op1)) break; if (expr_literal_int(op2) != 0) break; let val = comparison == EQ_EXPR ? Lattice.ZERO : Lattice.NONZERO; this.filter(state, op1, val, truth, isn); break; default: let exp = isn.operands()[0]; if (DECL_P(exp)) { this.filter(state, exp, Lattice.NONZERO, truth, isn); } } } // State transition for switch cases. Zero_NonZero.prototype.flowStateSwitch = function(isn, truth, state) { let exp = gimple_op(isn, 0); if (DECL_P(exp)) { if (truth != null) { this.filter(state, exp, truth, true, isn); } return; } throw new Error("ni"); } // Apply a filter to the state. We need to special case it for this analysis // because outparams only care about being a NULL pointer. Zero_NonZero.prototype.filter = function(state, vbl, val, truth, blame) { if (truth != true && truth != false) throw new Error("ni " + truth); if (truth == false) { val = negate(val); } state.filter(vbl, val, blame); } /** Update an ESP state so that vbl is predicated on val(arg). * i.e., vbl := absvalue(arg) == val */ Zero_NonZero.prototype.predicate = function(state, vbl, val, arg, blame) { let factory = state.factory; state.assignMapped(vbl, function(ss) { let rval = ss.get(arg); if (rval == undefined || rval == factory.TOP) { return undefined; } else if (rval == val) { return Lattice.NONZERO; } else { return Lattice.ZERO; } }, blame); } function meet (v1, v2) { // at this point possible values for v1, v2 // are: NONZERO, some int value if (v1 == Lattice.NONZERO && v2 != 0) return v2 if (v2 == Lattice.NONZERO && v1 != 0) return v1 if ([v1, v2].indexOf(Lattice.NONZERO) != -1) return ESP.NOT_REACHED if (v1 == v2) return v2 return ESP.NOT_REACHED } function join(v1, v2) { // Here we can assume that v1 != v2 and neither is TOP or NOT_REACHED. if (v1 == 0 || v2 == 0) return ESP.TOP; return Lattice.NONZERO; } dehydra-89ed48e70997/libs/util.js0000644000000000000000000001047511757635752016441 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Generic Dehydra utilities /** * Dump a Dehydra object in a readable tree format. * @param o object to dump * @param indent number of spaces to indent by * @param depth max nesting depth to dump * * Printing Dehydra objects tends to produce a big messy JSON-like * notation that's hard to read. This flattens things out and doesn't * go too deep, in order to reduce the amount of stuff to look at. */ function do_dehydra_dump(o, indent, depth) { if (depth == 0) return; try { for (var k in o) { var v = o[k]; if (typeof v == "string") { print_indented(k + ": '" + v + "'", indent); } else if (typeof v == "number") { print_indented(k + ": " + v, indent); } else if (typeof v == "boolean") { print_indented(k + ": " + v, indent); } else if (typeof v == "undefined") { print_indented(k + ": undefined", indent); } else { print_indented(k + ": " + 'object', indent); //print_indented(k + ": " + v.constructor.name, indent); do_dehydra_dump(v, indent + 1, depth - 1); } } } catch (e) { print_indented(e, indent); } } /** * Print string 's' indented by 'indent' spaces. */ function print_indented(s, indent) { for (var i = 0; i < indent; ++i) { s = " " + s; } print(s); } /** Pad the given string on the right with spaces to a total * length of 'pad'. */ function rpad(s, pad) { let n = pad - s.length; for (let i = 0; i < n; ++i) { s += ' '; } return s; } /** * Simpler API for dehydra dumping. @see do_dehydra_dump. */ function dehydra_dump(o) { print(typeof o); do_dehydra_dump(o, 1, 3); } // Need to have treehydra.h included for this to work /** Search a dehydra/treehydra JS object graph in BFS order. This is * useful to find the property access path to a given value that * you know is present, but is deeply buried. If search is successful, * the path the destination is printed to stdout. * * @param start object to start searching from. * @param test function of one argument--the search is for an * object for which this returns true. * @param count maximum number of objects to search. */ function dehydra_bfs(start, test, count) { if (!count) count = 100; let nodeMap = new Map(); let pdStart = { node: null, pred: null, field: null, dist: -1 }; let work = [ [ pdStart, null, start ] ]; while (work.length) { let [ pd, field, o ] = work.shift(); let d = nodeMap.get(o); if (d == undefined) { d = { node: o, pred: pd, field: field, dist: pd.dist + 1 }; nodeMap.put(o, d); if (test(o)) { shortest_path_dump(d); if (!--count) return; } try { for (var k in o) { var v = o[k]; work.push([d, k, v]); } } catch (e) { print(e); } } } print("not found"); } /** Helper for dehydra_bfs. */ function shortest_path_dump(d) { let code = ''; let path = []; while (d.field) { path.unshift(d); code = '.' + d.field + code; d = d.pred; } //print(code); code = ''; for each (let d in path) { code += '.' + d.field; print(code); print(' ' + dehydra_repr_short(d.node)); } } /** Return a short string representation of a dehydra/treehydra object, * giving just what kind of object it is. */ function dehydra_repr_short(o) { if (typeof o == 'string') return '"' + o + '"'; try { return o._struct_name; } catch (e) { } return 'tree ' + o.base.code.name; } dehydra-89ed48e70997/libs/xhydra.js0000644000000000000000000000337711757635752016766 0ustar rootroot00000000000000/* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // Code common to Treehydra and Dehydra. // Location is initialized from C and doesn't actually run function Location(_source_location, file, line, col) { } Location.prototype.toString = function() { with (this) { return file + ':' + line + (this.column != undefined ? ':' + column : ''); } } /** Report an error diagnostic in GCC. loc is optional. */ function error(msg, loc) { diagnostic(true, msg, loc ? loc : this._loc); } /** Report a warning diagnostic in GCC. loc is optional. */ function warning(msg, loc) { diagnostic(false, msg, loc ? loc : this._loc); } /** EnumValue kept here so that convert_tree.js sees it. */ function EnumValue (name, value) { this.name = name this.value = value } function print (msg, loc) { // this._loc is used by process in dehydra // scripts can set it to customize error reporting if (!loc) loc = this._loc if (loc) _print (loc + ": " + msg); else _print (msg); } dehydra-89ed48e70997/test/Makefile0000644000000000000000000000310411757635752016603 0ustar rootroot00000000000000include ../config.mk PLUGIN = ../gcc_dehydra.so PLUGIN_ARG = -fplugin-arg-gcc_dehydra-script GCCPLUGIN = -fplugin=$(PLUGIN) SCRIPT=test.js CXXFLAGS = $(GCCPLUGIN) $(PLUGIN_ARG)=$(SCRIPT) DEHYDRA_OBJECTS = \ test.o \ assign.o \ typedef.o \ operator_new.o \ destr_order.o \ init.o \ virtual.o \ constructor.o \ types.o \ templ-spec.o \ constructor.o \ stack_fieldOf.o \ templ-simple.o \ longevity.o \ finalizers.o \ $(NULL) GLOBAL_DEPS = \ $(MAKEFILE_LIST) \ $(PLUGIN) \ $(NULL) #do treehydra checks if treehydra is built by default check:: check_dehydra check_treehydra @printf "'make check' only runs the dehydra test suite. To run all tests, run 'make check_both'.\n" # Unit tests check_both: $(OBJECTS) python unit_test_harness.py both "$(CXX)" check_dehydra: $(OBJECTS) python unit_test_harness.py dehydra "$(CXX)" check_treehydra: python unit_test_harness.py treehydra "$(CXX)" check_both_tinderbox: $(OBJECTS) python unit_test_harness.py --tinderbox both "$(CXX)" %.o: %.cc $(PLUGIN) $(GLOBAL_DEPS) $(CXX) -c $(CXXFLAGS) $< -o /dev/null #tests workaround for outputing to stdout when shouldn't pipe: destr_order.cc $(GLOBAL_DEPS) $(CXX) -c $< $(CXXFLAGS) -pipe -o $@ rm $@ warning: test.cc $(GLOBAL_DEPS) $(CXX) -c $< $(GCCPLUGIN) $(PLUGIN_ARG)=warning.js -o $@ rm -f $@ tree: tree.js ../gcc_treehydra.so $(GLOBAL_DEPS) $(MAKE) PLUGIN=../gcc_treehydra.so SCRIPT=tree.js $(TEST) finalizer: finalizers.cc finalizers.js $(GLOBAL_DEPS) $(MAKE) PLUGIN=../gcc_treehydra.so SCRIPT=finalizers.js finalizers.o clean: rm -f *.o *~ a.out *.gcno dehydra-89ed48e70997/test/access.cc0000644000000000000000000000051311757635752016714 0ustar rootroot00000000000000class A { public: A(); protected: void f(); private: ~A(); static int static_member; }; class B : public A { private: using A::f; void f(int); }; class C : private A { int i; }; class D : A { int i; }; A* getA() { return 0; } B* getB() { return 0; } C *getC() { return 0; } D *getD() { return 0; } dehydra-89ed48e70997/test/access9.cc0000644000000000000000000000062111757635752017005 0ustar rootroot00000000000000// Copyright (C) 2003 Free Software Foundation // Contributed by Kriang Lerdsuwanakij // { dg-do compile } // Template instantiate during deferred access check template struct C { typedef int Y; }; template void f(typename T::X) { } class A { typedef int X; template friend void f(typename T::X); }; C<&f >::Y g(int); dehydra-89ed48e70997/test/array.cc0000644000000000000000000000004211757635752016566 0ustar rootroot00000000000000class Boo { const int i[10]; }; dehydra-89ed48e70997/test/assign.cc0000644000000000000000000000016211757635752016737 0ustar rootroot00000000000000class book { public: book(int i); int x; }; void foo () { book b(0); b = book(2); int i; i = b.x; } dehydra-89ed48e70997/test/attributes2.cc0000644000000000000000000000051611757635752017726 0ustar rootroot00000000000000#pragma GCC visibility push(hidden) template class __attribute__((user("default"))) A { public: T __attribute__((user(0))) i; }; struct Klass { }; struct Klass * __attribute__((user("value"))) varAttrOnType; A foo; typedef void MySpecialVoidType __attribute__((user("NS_ImSoSpecial"))); MySpecialVoidType *s; dehydra-89ed48e70997/test/bitfield.cc0000644000000000000000000000012611757635752017235 0ustar rootroot00000000000000struct foo { int i:30; unsigned char c:2; }; int f (foo *fo) { return fo->c; } dehydra-89ed48e70997/test/bitfields.cc0000644000000000000000000000012511757635752017417 0ustar rootroot00000000000000struct Test { int i; int j:22; unsigned int k:23; float m; long long n; }; dehydra-89ed48e70997/test/c_struct_prseg_reduced.c0000644000000000000000000000103711757635752022033 0ustar rootroot00000000000000/* Reduced from mozilla-central/nsprpub/pr/src/memory/prseg.c */ /* GCC 4.5.0 with cc1 defines a tree such that TYPE_LANG_SPECIFIC holds but LANG_TYPE_IS_CLASS does not. */ struct _IO_FILE { int _flags; char* _IO_read_ptr; char* _IO_read_end; char* _IO_read_base; char* _IO_write_base; char* _IO_write_ptr; char* _IO_write_end; char* _IO_buf_base; char* _IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers; void* _offset; void *__pad1; void *__pad2; }; dehydra-89ed48e70997/test/const.cc0000644000000000000000000000003711757635752016602 0ustar rootroot00000000000000class C { }; const C *f() { } dehydra-89ed48e70997/test/constructor.cc0000644000000000000000000000016311757635752020041 0ustar rootroot00000000000000struct A { A(); void foo(); }; struct B : A { void bar(); }; void Foo() { B b; b.foo(); //b.bar(); } dehydra-89ed48e70997/test/copy_ctor.cc0000644000000000000000000000034611757635752017460 0ustar rootroot00000000000000struct t { t(const char* v) { } t(const t& v){ // cerr << "copy2" << endl; } const char* data; }; t f(t v) {} t g(const t& v) {} int main(int argc, char **) { t a("test"); f(a); g(f(a)); a = f(a); } dehydra-89ed48e70997/test/csttype.cc0000644000000000000000000000015711757635752017152 0ustar rootroot00000000000000int f(int a1, int b1) { int a = 1; unsigned int b = 2u; long c = 3l; float d = 4.f; double e = 5.; } dehydra-89ed48e70997/test/ctor_node.cc0000644000000000000000000000007711757635752017434 0ustar rootroot00000000000000class C { int i; }; int f(); C createC() { return C(); } dehydra-89ed48e70997/test/decl.cc0000644000000000000000000000034211757635752016362 0ustar rootroot00000000000000int plain_var; typedef int some_typedef; template class FooTemplate { }; template class FooTemplate { }; template <> class FooTemplate { }; void forward_func_decl(); dehydra-89ed48e70997/test/destr_order.cc0000644000000000000000000000036611757635752017775 0ustar rootroot00000000000000class A { public: A(int& x) : mValue(x) {} ~A() { mValue--; } operator char**() { return 0; } private: int& mValue; }; void func(char **arg) {} int m=2; void test() { func(A(m)); if (m==1) m = 0; } int main() { test(); return(m); } dehydra-89ed48e70997/test/empty.cc0000644000000000000000000000000211757635752016602 0ustar rootroot00000000000000 dehydra-89ed48e70997/test/enum.cc0000644000000000000000000000007611757635752016423 0ustar rootroot00000000000000enum here { a, b, c=42 }; void foo() { here sJohnny = a; } dehydra-89ed48e70997/test/esp_lock.js0000644000000000000000000001147711757635752017314 0ustar rootroot00000000000000require({ version: '1.8' }); require({ after_gcc_pass: 'cfg' }); include('treehydra.js'); include('util.js'); include('gcc_util.js'); include('gcc_print.js'); include('unstable/adts.js'); include('unstable/analysis.js'); include('unstable/esp.js'); var Zero_NonZero = {} include('unstable/zero_nonzero.js', Zero_NonZero); // Names of functions to check lock/unlock protocol on CREATE_FUNCTION = 'create_mutex'; LOCK_FUNCTION = 'lock'; UNLOCK_FUNCTION = 'unlock'; MapFactory.use_injective = true; // Print a trace for each function analyzed let TRACE_FUNCTIONS = 0; // Trace operation of the ESP analysis, use 2 or 3 for more detail let TRACE_ESP = 0; // Print time-taken stats let TRACE_PERF = 0; function process_tree(fndecl) { // At this point we have a function we want to analyze if (TRACE_FUNCTIONS) { print('* function ' + decl_name(fndecl)); print(' ' + loc_string(location_of(fndecl))); } if (TRACE_PERF) timer_start(fstring); let cfg = function_decl_cfg(fndecl); let a = new LockCheck(cfg, TRACE_ESP); a.run(); if (TRACE_PERF) timer_stop(fstring); } // Abstract values. ESP abstract values should have a 'ch' field, // which is a single-character label used in traces. function AbstractValue(name, ch) { this.name = name; } AbstractValue.prototype.toString = function() { return this.name + ' (' + this.name[0] + ')'; } AbstractValue.prototype.toShortString = function() { return this.name[0] } // Our actual abstract values. // Abstract values for lock status let av = {}; for each (let name in ['LOCKED', 'UNLOCKED']) { av[name] = new AbstractValue(name); } /** Meet function for our abstract values. */ av.meet = function(v1, v2) { // At this point we know v1 != v2. let values = [v1,v2] if (values.indexOf(av.LOCKED) != -1 || values.indexOf(av.UNLOCKED) != -1) return ESP.NOT_REACHED; return Zero_NonZero.meet(v1, v2) } function LockCheck(cfg, trace) { // Property variable list. ESP will track full tuples of abstract // states for these variables. let psvar_list = []; // Scan for mutex vars and put them in the list. This isn't particularly // complete, it just works for the example. let found = create_decl_set(); // ones we already found for (let bb in cfg_bb_iterator(cfg)) { for (let isn in bb_isn_iterator(bb)) { if (gimple_code(isn) != GIMPLE_CALL) continue let fname = decl_name(gimple_call_fndecl(isn)) if (fname != CREATE_FUNCTION) continue let arg = gimple_call_arg(isn, 0); if (!DECL_P(arg) || found.has(arg)) continue; found.add(arg); psvar_list.push(new ESP.PropVarSpec(arg, true)); } } this.zeroNonzero = new Zero_NonZero.Zero_NonZero() ESP.Analysis.call(this, cfg, psvar_list, av.meet, trace); } LockCheck.prototype = new ESP.Analysis; // State transition function. Mostly, we delegate everything to // another function as either an assignment or a call. LockCheck.prototype.flowState = function(isn, state) { switch (gimple_code(isn)) { case GIMPLE_SWITCH: case GIMPLE_COND: // This gets handled by flowStateCond instead, has no exec effect break; case GIMPLE_CALL: if (this.processCall(isn, isn, state)) break; default: this.zeroNonzero.flowState(isn, state) } } // State transition function to apply branch filters. This is kind // of boilerplate--we're just handling some stuff that GCC generates. LockCheck.prototype.flowStateCond = function(isn, truth, state) { this.zeroNonzero.flowStateCond (isn, truth, state) } LockCheck.prototype.processCall = function(gs, blame, state) { let fname = decl_name(gimple_call_fndecl(gs)); if (fname == LOCK_FUNCTION) { this.processLock(gs, av.LOCKED, av.UNLOCKED, blame, state); } else if (fname == UNLOCK_FUNCTION) { this.processLock(gs, av.UNLOCKED, av.LOCKED, blame, state); } else if (fname == CREATE_FUNCTION) { this.processLock(gs, av.UNLOCKED, undefined, blame, state); } else { return false } return true }; // State transition for a lock API. LockCheck.prototype.processLock = function(call, val, precond, blame, state) { let arg = gimple_call_arg(call, 0); if (DECL_P(arg)) { if (precond != undefined) this.checkPrecondition(arg, precond, blame, state); state.assignValue(arg, val, blame); } else { warning("applying lock to a nonvariable so we don't check it", location_of(blame)); } }; // Check that a precondition is held. LockCheck.prototype.checkPrecondition = function(vbl, precond, blame, state) { for (let substate in state.substates.getValues()) { this.checkSubstate(vbl, precond, blame, substate); } } LockCheck.prototype.checkSubstate = function(vbl, precond, blame, ss) { let val = ss.get(vbl); if (val != precond) { error("precondition not met: expected " + precond + ", got " + val, location_of(blame)); } } dehydra-89ed48e70997/test/explicit.cc0000644000000000000000000000025211757635752017274 0ustar rootroot00000000000000class C { public: C(); C(int); explicit C(char); C(double, int i = 5); explicit C(double, char c = 5); C(int, char); explicit C(int, float); }; dehydra-89ed48e70997/test/extern_c.cc0000644000000000000000000000013011757635752017255 0ustar rootroot00000000000000 extern "C" { void c_function(); int c_int; } void cc_function(); int cc_int; dehydra-89ed48e70997/test/finalizers.cc0000644000000000000000000000045111757635752017622 0ustar rootroot00000000000000namespace MMgc { class GCFinalizable { }; }; struct nsFoo { ~nsFoo(); }; class nsBar : public MMgc::GCFinalizable { void v(); void __attribute__((user("finalizer_safe"))) safe_func() { v(); } public: ~nsBar() { safe_func(); } nsFoo foo; }; void v () { nsBar bar; } dehydra-89ed48e70997/test/finalizers.js0000644000000000000000000000213211757635752017647 0ustar rootroot00000000000000include ("treehydra.js") function is_GCFinalizable (record_type) { function f (record_type) { return IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (record_type))) == "GCFinalizable" } return walk_hierarchy (f, record_type) } function is_finalizer_safe (function_decl) { return get_user_attribute (DECL_ATTRIBUTES (function_decl)) == "finalizer_safe" } function process_tree(function_decl) { if (!is_finalizer_safe (function_decl)) { var context = DECL_CONTEXT (function_decl) if (!context || TREE_CODE (context) != RECORD_TYPE || !is_GCFinalizable (context)) return } var body = DECL_SAVED_TREE (function_decl) var uniqueness_map = new Map() function walker(t, stack) { var code = TREE_CODE (t) switch (code) { case CALL_EXPR: var fn = CALL_EXPR_FN (t) if (TREE_CODE (fn) == ADDR_EXPR) fn = TREE_OPERAND (fn, 0) if (!is_finalizer_safe (fn)) error ("Calling a function that hasn't been annotated as finalizer_safe:" +decl_name(fn)) break; default: } } walk_tree (body, walker, uniqueness_map) } dehydra-89ed48e70997/test/hasDefault.cc0000644000000000000000000000061611757635752017537 0ustar rootroot00000000000000class C { public: void a(); void b(int i); void c(int i = 1); void d(int i, char f = 2, float g = 3); static void s_a(); static void s_b(int i); static void s_c(int i = 1); static void s_d(int i, char f = 2, float g = 3); }; void f(int i) { } void g(int i = 1) { } void h(int i, char f = 2, float g = 3) { } void i(int i = 1, char f = 2, float g = 3) { } dehydra-89ed48e70997/test/hasOwn.cc0000644000000000000000000000002011757635752016703 0ustar rootroot00000000000000void foo() { } dehydra-89ed48e70997/test/incdec.cc0000644000000000000000000000013411757635752016677 0ustar rootroot00000000000000int prepp, premm, postpp, postmm; void foo () { ++prepp; --premm; postpp++; postmm--; } dehydra-89ed48e70997/test/inheritance.cc0000644000000000000000000000010411757635752017740 0ustar rootroot00000000000000class Base { }; class Derived : public Base { }; Derived derived; dehydra-89ed48e70997/test/init.cc0000644000000000000000000000012711757635752016417 0ustar rootroot00000000000000class F; class Klass { F *f; int depth; Klass (F *fin):f(fin), depth(0) { } }; dehydra-89ed48e70997/test/intlit.cc0000644000000000000000000000072411757635752016762 0ustar rootroot00000000000000void f() { unsigned int m = 1; unsigned int n = 4294967295u; int o = -10; int p = 12; long q = 0; long long r = 0; unsigned rr = 0; unsigned long s = 0; unsigned long long t = 0; int u = -0x3fffffd6; long v = -0x3fffffd6; long long w = -0x3fffffd6; int x = 0x4000002a; long y = 0x4000002a; long long z = 0x4000002a; long long a = -0x1000000000009ll; long long b = 0x1000000000009ll; unsigned long long c = 0x800000000000004dull; } dehydra-89ed48e70997/test/isExtern.cc0000644000000000000000000000015311757635752017254 0ustar rootroot00000000000000struct A { int a; static int b; static int c; void f(); void g(); }; int A::c; void A::g() { } dehydra-89ed48e70997/test/iterate_vars.cc0000644000000000000000000000023011757635752020137 0ustar rootroot00000000000000class C { public: C() { } C(const C&) { } ~C() { } }; const C& bar(const C& c) { return c; } void foo() { bar(C(bar(C(bar(C()))))); } dehydra-89ed48e70997/test/lazy_atts.cc0000644000000000000000000000014311757635752017464 0ustar rootroot00000000000000void f(const char *s, ...) __attribute__((format(printf, 1, 2))); void f(const char *s, ...) { } dehydra-89ed48e70997/test/lib_error.js0000644000000000000000000000036611757635752017467 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'empty.cc', 'output': 'stderr_has("TypeError", "/gcc_compat.js")', 'lang': 'c,c++' } // Produce an error in an included library file in the treehydra dir. include('treehydra.js'); let c = TREE_CODE(undefined);dehydra-89ed48e70997/test/location.cc0000644000000000000000000000006211757635752017262 0ustar rootroot00000000000000void f(); void g() { while (1) { f(); } } dehydra-89ed48e70997/test/locks.h0000644000000000000000000000013311757635752016426 0ustar rootroot00000000000000struct mutex {}; void lock(mutex *m); void unlock(mutex *m); void create_mutex(mutex *m); dehydra-89ed48e70997/test/locks_bad1.cc0000644000000000000000000000023411757635752017455 0ustar rootroot00000000000000#include "locks.h" void bad1(int doBadStuff) { mutex *m = new mutex; create_mutex(m); lock(m); if (doBadStuff) { unlock(m); } unlock(m); } dehydra-89ed48e70997/test/locks_bad2.cc0000644000000000000000000000027511757635752017463 0ustar rootroot00000000000000#include "locks.h" void bad2(int doBadStuff) { mutex *m = new mutex; create_mutex(m); lock(m); if (doBadStuff) { switch (doBadStuff) { case 2: lock(m); } } } dehydra-89ed48e70997/test/locks_bad3.cc0000644000000000000000000000022711757635752017461 0ustar rootroot00000000000000#include "locks.h" void bad3(int doBadStuff) { mutex *m = new mutex; create_mutex(m); lock(m); while (doBadStuff--) { unlock(m); } } dehydra-89ed48e70997/test/locks_bad4.cc0000644000000000000000000000030111757635752017453 0ustar rootroot00000000000000#include "locks.h" void bad3(int doBadStuff) { mutex *m = new mutex; create_mutex(m); lock(m); doBadStuff = 3; if (doBadStuff == 0) { unlock(m); } else { lock(m); } } dehydra-89ed48e70997/test/locks_good.cc0000644000000000000000000000062411757635752017601 0ustar rootroot00000000000000#include "locks.h" void good1(int x) { mutex *m = new mutex; create_mutex(m); lock(m); unlock(m); if (!x) { switch (x) { case 3: unlock(m); break; default: break; } } } void foo(); void bar(); void good2(int doLock) { mutex *m = new mutex; if (doLock) { create_mutex(m); lock(m); } foo(); bar(); if (doLock) { unlock(m); } } dehydra-89ed48e70997/test/locks_good2.cc0000644000000000000000000000025611757635752017664 0ustar rootroot00000000000000#include "locks.h" class C { public: ~C() {} }; void good(int x) { mutex *m = new mutex; create_mutex(m); if (x) lock(m); C c; if (!x) return; unlock(m); } dehydra-89ed48e70997/test/members.cc0000644000000000000000000000035111757635752017105 0ustar rootroot00000000000000class Outer { class Inner { int a; class MostInner { int b; }; }; int a; static int sa; void foo(); static void sfoo(); enum { DO_IT = 1 }; int Outer::* ptrto_mem() { return &Outer::a; } }; dehydra-89ed48e70997/test/namespace.cc0000644000000000000000000000007111757635752017406 0ustar rootroot00000000000000namespace Namespace { class Bah { }; Bah bah; }; dehydra-89ed48e70997/test/numinfo.cc0000644000000000000000000000113011757635752017122 0ustar rootroot00000000000000#include #if !defined __cplusplus #include #include #define bool _Bool #endif // these are "recommended" in standard, but they are in gcc and unlikely to // disappear int8_t int8var; int16_t int16var; int32_t int32var; int64_t int64var; uint8_t uint8var; uint16_t uint16var; uint32_t uint32var; uint64_t uint64var; bool boolvar; float floatvar; double doublevar; enum enumt { a, b, c=7 }; #if !defined __cplusplus enum #endif enumt enumvar; #if defined __cplusplus float &refvar = floatvar; #else float *refvar = &floatvar; #endif void *pointervar = &enumvar; dehydra-89ed48e70997/test/onefunc.cc0000644000000000000000000000010711757635752017107 0ustar rootroot00000000000000char *foo(int a, float b, char *p) { p += a + (int) b; return p; } dehydra-89ed48e70997/test/operator_new.cc0000644000000000000000000000004711757635752020161 0ustar rootroot00000000000000class Foo {}; void f () { new Foo; } dehydra-89ed48e70997/test/operator_not.cc0000644000000000000000000000006211757635752020165 0ustar rootroot00000000000000class Foo { #define FOO bah int FOO(int i); }; dehydra-89ed48e70997/test/parameters.cc0000644000000000000000000000003311757635752017613 0ustar rootroot00000000000000int myfunc(int i, char j); dehydra-89ed48e70997/test/print-functions.js0000644000000000000000000000102011757635752020636 0ustar rootroot00000000000000function cleanup (v) { delete v.type delete v.loc if (v.fieldOf) cleanup (v.fieldOf) if (v.arguments) v.arguments = Array.map (v.arguments, cleanup) return v } function processVar(v) { cleanup(v) print(v); } function iter_over_inits(vars) { var v, va; for each(v in vars) { if (v.isFcall) processVar(v); iter_over_inits(v.assign); iter_over_inits(v.arguments); } } /** called by dehydra on every "statement" */ function process(vars, state) { iter_over_inits(vars); } dehydra-89ed48e70997/test/return_expr.cc0000644000000000000000000000004711757635752020032 0ustar rootroot00000000000000int foo() { int x = 0; return x; } dehydra-89ed48e70997/test/rw_exns.js0000644000000000000000000000220511757635752017167 0ustar rootroot00000000000000let succ_count = 0; let fail_count = 0; // Make a unit tester that tests for whether an exception is thrown function make_unit_test_exn(fn, name) { return function() { if (arguments.length < 1) throw new Error(); let should_throw = arguments[0]; let args = [ arguments[k] for (k in range(1, arguments.length)) ] print("test: " + name + " " + args); let threw = false; try { fn.apply(this, args); } catch (e) { print(" exception: " + e); threw = true; } check(should_throw, threw); } } function range(start, end) { for (let i = start; i < end; ++i) yield i; } // Check a test result function check(expected, actual) { if (expected == actual) { ++succ_count; print(" PASS"); } else { ++fail_count; print(" FAIL"); } } function print_stats() { print("\nTest results:"); print(" Passed: " + succ_count); print(" Failed: " + fail_count); } wf = make_unit_test_exn(write_file); rf = make_unit_test_exn(read_file); wf(true); wf(true, 3); wf(true, 'a/z', 'aa'); wf(false, 'out', 'a'); rf(true); rf(true, 'asdflkj'); rf(false, 'out'); print_stats();dehydra-89ed48e70997/test/scopes.cc0000644000000000000000000000015011757635752016744 0ustar rootroot00000000000000namespace NS { class Outer { class Inner { }; }; } class Outer2 { class Inner2 { }; }; dehydra-89ed48e70997/test/semantic_error.js0000644000000000000000000000024311757635752020516 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'stderr_has("TypeError")', 'lang': 'c,c++' } // Produce a TypeError. let x = undefined; x.y = 7;dehydra-89ed48e70997/test/skipthis.cc0000644000000000000000000000017611757635752017316 0ustar rootroot00000000000000struct C { void f(int a, int b); static void g(C *o, int a); }; C f() { } typedef void (*fp)(int a, int b); fp g() { } dehydra-89ed48e70997/test/stack_fieldOf.cc0000644000000000000000000000020411757635752020205 0ustar rootroot00000000000000/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ struct A { A(int i); int i; }; void Func() { A a = 3; } dehydra-89ed48e70997/test/stringlit.cc0000644000000000000000000000021111757635752017465 0ustar rootroot00000000000000void f() { const char* a = "a-test"; const char* b = "b-te\0st"; const wchar_t* c = L"c-test"; const wchar_t* d = L"d-te\0st"; } dehydra-89ed48e70997/test/subdir/lib.js0000644000000000000000000000002411757635752017535 0ustar rootroot00000000000000LIB_INCLUDED = true;dehydra-89ed48e70997/test/subdir/main.js0000644000000000000000000000013411757635752017715 0ustar rootroot00000000000000include('lib.js'); if (this.LIB_INCLUDED) { print("OK"); } else { print("Fail"); } dehydra-89ed48e70997/test/syntax_error.js0000644000000000000000000000022311757635752020237 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'stderr_has("SyntaxError")', 'lang': 'c,c++' } // Produce a syntax error if (3dehydra-89ed48e70997/test/tc_pass1.js0000644000000000000000000000052311757635752017220 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'onefunc.cc', 'output': 'stderr_empty', 'lang': 'c,c++' } // Test that pass option is accepted and we get CFG // Should print: 0 include('treehydra.js'); require({ 'after_gcc_pass': 'cfg' }); function process_tree(fndecl) { let bb = fndecl.function_decl.f.cfg.x_entry_block_ptr; print(bb.index); }dehydra-89ed48e70997/test/tc_pass2.js0000644000000000000000000000042711757635752017224 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'onefunc.cc', 'output': 'stderr_has("Cannot set gcc_pass_after")', 'lang': 'c,c++' } // Test that pass option is not accepted in process_tree. include('treehydra.js'); function process_tree(fndecl) { require({ 'after_gcc_pass': 'cfg' }); }dehydra-89ed48e70997/test/temp_destr.cc0000644000000000000000000000034011757635752017617 0ustar rootroot00000000000000struct A { A(); A(const A &other); ~A(); }; void FunctionTakesA(A a); void TestFunc(A &a) { // this should create a stack temporary A and invoke the copy-constructor // and the destructor. FunctionTakesA(a); } dehydra-89ed48e70997/test/templ-func.cc0000644000000000000000000000013611757635752017526 0ustar rootroot00000000000000template T myadd(T a, T b) { return a + b; } int main() { return myadd(2, 3); } dehydra-89ed48e70997/test/templ-func2.cc0000644000000000000000000000033611757635752017612 0ustar rootroot00000000000000class Base { public: virtual void F() = 0; }; template class A : public Base { public: virtual void F(); }; template void A::F() { int i = 1 + 1; } Base* testfunc() { return new A(); } dehydra-89ed48e70997/test/templ-func3.cc0000644000000000000000000000023111757635752017605 0ustar rootroot00000000000000class Class; template int TemplateFunction(A a) { Class* k; k = 0; return a == A() ? 1 : 0; } void foo(void) { TemplateFunction(1); } dehydra-89ed48e70997/test/templ-simple.cc0000644000000000000000000000014611757635752020065 0ustar rootroot00000000000000class Klass { }; template class AutoFoo { }; AutoFoo<40, AutoFoo<60, Klass> > c; dehydra-89ed48e70997/test/templ-spec.cc0000644000000000000000000000026111757635752017524 0ustar rootroot00000000000000template class Array { T a[e]; }; template class Array { E *a; }; template<> class Array { }; class B { }; Array p; dehydra-89ed48e70997/test/templ.cc0000644000000000000000000000015111757635752016572 0ustar rootroot00000000000000template struct templ { int i; }; class Foo { }; int test (templ *f) { return f->i; } dehydra-89ed48e70997/test/template_member.cc0000644000000000000000000000012111757635752020610 0ustar rootroot00000000000000struct A { template void f(B); template void g(); }; dehydra-89ed48e70997/test/template_union.cc0000644000000000000000000000013211757635752020473 0ustar rootroot00000000000000template union foo { enum {value = T}; }; int main() {return foo<42>::value;} dehydra-89ed48e70997/test/test.cc0000644000000000000000000000034511757635752016435 0ustar rootroot00000000000000 int id; namespace boo { class book2 { public: book2(int i); }; class book2; typedef book2 book; int foo () { static int fook=0; id-=fook++; int i = 9, x = 6; book b(foo()); return 0; } } dehydra-89ed48e70997/test/test.js0000644000000000000000000000226311757635752016465 0ustar rootroot00000000000000require({ after_gcc_pass: 'cfg'}); include('treehydra.js'); include('unstable/analysis.js'); include('gcc_util.js'); include('util.js'); include('gcc_print.js'); function iterate_decls(cfg) { for (let bb in cfg_bb_iterator(cfg)) { for (let isn in bb_isn_iterator(bb)) { for (let decl in isn_uses(isn)) { if (DECL_P(decl)) yield decl; } for (let decl in isn_defs(isn)) { if (DECL_P(decl)) yield decl; } } } } results = {} function process_tree(fndecl) { let cfg = function_decl_cfg(fndecl); // cfg_dump(cfg); for (let decl in iterate_decls(cfg)) { let context = DECL_CONTEXT(decl); if (!DECL_NAME(decl)) continue; if (context) switch (context.tree_code()) { case FUNCTION_DECL: case RECORD_TYPE: // static class and function vars are special if (TREE_STATIC(decl)) break; continue; } results[IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(fndecl)) + " uses " + IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl)) + " " + decl_name(decl)] = 1; } //isn_uses; } function input_end() { for (let v in results) print(v) } dehydra-89ed48e70997/test/test_arg_key_value45.js0000644000000000000000000000077011757635752021534 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'empty.cc', 'output': 'unit_test', 'args': { 'blah': 'bleh', 'bip': 'bop' }, 'version': '4.5' } include('unit_test.js'); let r = new TestResults(); function EqualsTest(v1, v2) { this._v1 = v1; this._v2 = v2; } EqualsTest.prototype = new TestCase(); EqualsTest.prototype.runTest = function() { this.assertEquals(this._v1, this._v2); } new EqualsTest(options.blah, "bleh").run(r); new EqualsTest(options.bip, "bop").run(r); function input_end() { r.list(); } dehydra-89ed48e70997/test/test_attributes.js0000644000000000000000000000063611757635752020735 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'attributes2.cc', 'output': 'unit_test', 'lang': 'c++' } function process_type(c) { //print (c) } function process_function(f,arr) { //print(arr) } function process_decl(v) { if (v.name == "varAttrOnType") { var a = v.type.attributes[0] if (a.name != "user" || a.value[0] != "value") throw new Error ("Attributes on types don't work") print("OK") } } dehydra-89ed48e70997/test/test_bitfields.js0000644000000000000000000000375111757635752020515 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'bitfields.cc', 'output': 'unit_test' } // Test that bitfields are represented correctly include('unit_test.js'); /** * Check that a property path leads to an expected value. * * @param v The object to check * @param path An array of properties (strings or numbers) to follow * @param expected The value that is expected... checked with == */ function TestProperty(v, path, expected) { this._v = v; this._path = path; this._expected = expected; } TestProperty.prototype = new TestCase(); TestProperty.prototype.runTest = function() { let v = this._v; for each (let p in this._path) { v = v[p]; } this.assertEquals(v, this._expected); }; let tests = [ [['members', 0, 'name'], 'Test::i'], [['members', 0, 'type', 'name'], 'int'], [['members', 0, 'type', 'bitfieldBits'], undefined], [['members', 0, 'type', 'bitfieldOf'], undefined], [['members', 1, 'name'], 'Test::j'], [['members', 1, 'type', 'name'], 'int:22'], [['members', 1, 'type', 'bitfieldBits'], 22], [['members', 1, 'type', 'bitfieldOf', 'name'], 'int'], [['members', 2, 'name'], 'Test::k'], [['members', 2, 'type', 'name'], 'unsigned int:23'], [['members', 2, 'type', 'bitfieldBits'], 23], [['members', 2, 'type', 'bitfieldOf', 'name'], 'unsigned int'], [['members', 3, 'name'], 'Test::m'], [['members', 3, 'type', 'name'], 'float'], [['members', 4, 'name'], 'Test::n'], [['members', 4, 'type', 'name'], 'long long int'], [['members', 4, 'type', 'bitfieldBits'], undefined] ]; let r = new TestResults(); function process_type(t) { if (t.name != 'Test') return; for each (test in tests) { let [path, expected] = test; new TestProperty(t, path, expected).run(r); } } function input_end() { r.verifyExpectedTestsRun(tests.length) } dehydra-89ed48e70997/test/test_c_struct_class_key.js0000644000000000000000000000050211757635752022422 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'c_struct_prseg_reduced.c', 'output': 'unit_test', 'lang': 'c,c++' } include('unit_test.js'); let test = new TestCase(); function process_tree_type(type) { let type = class_key_or_enum_as_string(type); test.assertEquals(type, "struct"); } function input_end() { print('OK'); } dehydra-89ed48e70997/test/test_const.js0000644000000000000000000000130011757635752017662 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'const.cc', 'output': 'unit_test', 'lang': 'c++' } // Test that 'const' is handled correctly. include('unit_test.js'); let r = new TestResults(); // Test is constructed so all function types should have 2 args. function ConstTestCase(type) { this.type = type; } ConstTestCase.prototype = new TestCase(); ConstTestCase.prototype.runTest = function() { this.assertTrue(this.type.isPointer); let type = this.type.type; this.assertTrue(type.isConst); this.assertEquals(type.name, "C"); }; function process_function(decl, body) { let ret_type = decl.type.type; new ConstTestCase(ret_type).run(r); } function input_end() { r.verifyExpectedTestsRun(1) } dehydra-89ed48e70997/test/test_copy_ctor.js0000644000000000000000000000244111757635752020544 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'copy_ctor.cc', 'output': 'unit_test', 'lang': 'c++' } let failed = false let run = 0 const spec = [[/^t::t/, 1], [/^f/, 1, true], [/^g/, 1] ] function find_ctor_calls(loc, v) { if (v.isFcall) { warning("# Fcall '" + v.name + "' with " + v.arguments.length + " args" /*+ " memberOf" + v.memberOf*/); for each (var arg in v.arguments) { if (arg.isConstructor) warning("Constructor '" + arg.name + "' used in" + "argument to '" + v.name + "'"); find_ctor_calls(loc, arg); } for each (let [regexp, len, arg_constructor] in spec) { if (regexp.exec(v.name)) { run++; if (v.arguments.length != len) { error(v.name + " has the wrong number of arguments", loc); failed = true } if (arg_constructor && !v.arguments[0].isConstructor) { error(v.name + " expects a copy constructor as argument", loc) failed = true } } } } if (v.assign) { for each (var v2 in v.assign) find_ctor_calls(loc, v2); } } function process_function(decl, body) { for each (var stmts in body) for each (var stmt in stmts.statements) find_ctor_calls(stmts.loc, stmt); } function input_end() { if (!failed && run) print("OK") } dehydra-89ed48e70997/test/test_cp_pre.js0000644000000000000000000000102611757635752020011 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'assign.cc', 'output': 'unit_test', 'lang': 'c++' } include("gcc_util.js") let counter = 2 function process_cp_pre_genericize(fn) { let ls = BIND_EXPR_BODY(DECL_SAVED_TREE(fn).tree_check(BIND_EXPR)).tree_check(STATEMENT_LIST) for (let s in iter_statement_list(ls)) { //print(s.tree_code()) } --counter; } function process_tree_decl(decl) { if (decl_name(decl) == "foo" && decl.tree_check(FUNCTION_DECL)) --counter } function input_end() { if (counter == 0) print("OK") } dehydra-89ed48e70997/test/test_csttype.js0000644000000000000000000000157411757635752020244 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'csttype.cc', 'output': 'unit_test' } // test that .type is properly setup on constant nodes include('unit_test.js') let r = new TestResults(); function CstDotTypeTestCase(cst, expType, expTypeName) { this.cst = cst; this.expType = expType; this.expTypeName = expTypeName; } CstDotTypeTestCase.prototype = new TestCase(); CstDotTypeTestCase.prototype.runTest = function () { this.assertTrue(this.cst.type === this.expType); this.assertEquals(this.cst.type.name, this.expTypeName); } let expNames = [ 'int', 'unsigned int', 'long int', 'float', 'double' ]; function process_function(decl, body) { for (let i = 0; i < expNames.length; ++i) { let stmt = body[i].statements[0]; new CstDotTypeTestCase(stmt.assign[0], stmt.type, expNames[i]).run(r); } } function input_end() { r.verifyExpectedTestsRun(expNames.length) } dehydra-89ed48e70997/test/test_dehydra_location.js0000644000000000000000000000050311757635752022050 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'location.cc', 'output': 'unit_test' } include('util.js'); function process_function(decl, body) { for each (var stmts in body) { for each (var stmt in stmts.statements) { if (stmt.isFcall && stmt.name == "f()" && stmts.loc.line == 4) { print('OK'); } } } } dehydra-89ed48e70997/test/test_diag_loc.js0000644000000000000000000000032711757635752020305 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'onefunc.cc', 'output': 'stderr_has(":1:")' } // Error message should refer to line 1. function process_function(decl, body) { print(decl.loc); error("test_error", decl.loc); }dehydra-89ed48e70997/test/test_diag_loc_t.js0000644000000000000000000000033311757635752020625 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'onefunc.cc', 'output': 'stderr_has(":1:")', 'lang': 'c,c++' } // Error message should refer to line 1. function process_tree(fndecl) { error("test_error", location_of(fndecl)); } dehydra-89ed48e70997/test/test_enum.js0000644000000000000000000000232111757635752017504 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'enum.cc', 'output': 'unit_test' } require({ after_gcc_pass: "cfg" }); include('unstable/lazy_types.js') include('unit_test.js') let r = new TestResults(); function EnumTestCase(type) { this.type = type; } EnumTestCase.prototype = new TestCase(); EnumTestCase.prototype.runTest = function () { let type = this.type; this.assertEquals(type.kind, 'enum'); this.assertEquals(type.name, 'here'); this.assertEquals(type.members.length, 3); let m = type.members; this.assertEquals(m[0].name, 'a'); this.assertEquals(m[0].value, 0); this.assertEquals(m[1].name, 'b'); this.assertEquals(m[1].value, 1); this.assertEquals(m[2].name, 'c'); this.assertEquals(m[2].value, 42); } // Test that 'enum' type is passed correctly function process_type(type) { new EnumTestCase(type).run(r); } // Test enum handling of dehydra_convert function process_tree(fn) { let cfg = function_decl_cfg(fn); for (let isn in cfg_isn_iterator(cfg)) { if (isn.tree_code() != GIMPLE_ASSIGN) continue; let lhs = gimple_op(isn, 0); let t = TREE_TYPE(lhs); t = dehydra_convert(t); new EnumTestCase(t).run(r); } } function input_end() { r.verifyExpectedTestsRun(2); } dehydra-89ed48e70997/test/test_explicit.js0000644000000000000000000000256111757635752020367 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'explicit.cc', 'output': 'unit_test' } // Test for correct results for hasDefault include('unit_test.js'); let r = new TestResults(); function TestDefaults(member, value) { this.member = member; this.value = value; } TestDefaults.prototype = new TestCase(); TestDefaults.prototype.description = function() { return "isExplicit " + this.member.name + ": " } TestDefaults.prototype.runTest = function() { this.assertEquals(!!this.member.isExplicit, this.value); }; let tests={'C::C()': false, 'C::C(int)': false, 'C::C(char)': true, 'C::C(double, int)': false, 'C::C(double, char)': true, 'C::C(int, char)': false, 'C::C(int, float)': true}; if (/^4.3/.exec(sys.gcc_version)) { delete tests['C::C()'] delete tests['C::C(int, char)'] } function getMember(t, name) { for each (let m in t.members) { if (m.name == name) return m; } throw Error("Member " + name + " not found."); } function process_type(t) { if (t.name == 'C') { for (let name in tests) new TestDefaults(getMember(t, name), tests[name]).run(r); } } function process_function(d, b) { new TestDefaults(d, getValues(d.shortName, func_tests)).run(r); } function input_end() { let testCount = 0; for (name in tests) testCount++; r.verifyExpectedTestsRun(testCount); } dehydra-89ed48e70997/test/test_extern_c.js0000644000000000000000000000107011757635752020347 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'extern_c.cc', 'output': 'unit_test' } function assert (t) { if (!t) throw new Error("isExternC is busted!"); } let decls = {"c_function()":true, "c_int":true, "cc_function()":undefined, "cc_int":undefined} function process_decl(v) { if (v.name in decls) { isExternC = decls[v.name] print(v.isExternC) if(isExternC) assert(v.isExternC) else assert(!v.isExternC) delete decls[v.name] } } function input_end() { if (decls.toString() == {}.toString()) print("OK"); } dehydra-89ed48e70997/test/test_hasDefault.js0000644000000000000000000000347011757635752020626 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'hasDefault.cc', 'output': 'unit_test' } // Test for correct results for hasDefault include('unit_test.js'); let r = new TestResults(); function TestDefaults(member, values) { this.member = member; this.values = values; } TestDefaults.prototype = new TestCase(); TestDefaults.prototype.runTest = function() { let params = this.member.parameters; this.assertEquals(params.length, this.values.length); for (let i = 0; i < params.length; i++) this.assertEquals(params[i].hasDefault, this.values[i]); }; function getMember(t, mname) { let test = t.name + '::' + mname; for each (let m in t.members) { if (m.name.indexOf(test) != -1) return m; } throw Error("Member " + test + " not found."); } function getValues(n, tests) { for each (let t in tests) { if (t[0] == n) return t[1]; } throw Error("Function " + n + " not found."); } let type_tests = [['a', [undefined]], ['b', [undefined, undefined]], ['c', [undefined, true]], ['d', [undefined, undefined, true, true]], ['s_a', []], ['s_b', [undefined]], ['s_c', [true]], ['s_d', [undefined, true, true]]]; let func_tests = [['f', [undefined]], ['g', [true]], ['h', [undefined, true, true]], ['i', [true, true, true]]]; function process_type(t) { if (t.name == 'C') { for each (let [name, values] in type_tests) new TestDefaults(getMember(t, name), values).run(r); } } function process_function(d, b) { new TestDefaults(d, getValues(d.shortName, func_tests)).run(r); } function input_end() { let testCount = type_tests.length + func_tests.length; r.verifyExpectedTestsRun(testCount); } dehydra-89ed48e70997/test/test_hasOwnProperty.js0000644000000000000000000000047011757635752021547 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'hasOwn.cc', 'output': 'unit_test', 'lang': 'c++' } // See bug 570195 for SpiderMonkey fix function hasOwn(x) { return x.hasOwnProperty("blah"); } function process_tree(fn) { // ok fn.hasOwnProperty("blah"); // unhandledLazyProperty hasOwn(fn); print ("OK") } dehydra-89ed48e70997/test/test_hashcode.js0000644000000000000000000000070411757635752020321 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } function assert (t) { if (!t) throw new Error("hashcode is busted"); } // primtives are left intact assert (hashcode("str") === "str") assert (hashcode(true) === true) const h = hashcode(this) assert (hashcode({}) != hashcode({})) assert (hashcode(this) == h) assert (hashcode(this) != this) assert (hashcode(this) != hashcode(assert)) print("OK") dehydra-89ed48e70997/test/test_incdec.js0000644000000000000000000000132511757635752017770 0ustar rootroot00000000000000// { 'test' : 'dehydra', 'input' : 'incdec.cc', 'output': 'unit_test' } // Test for prefix and postfix -- and ++ include('unit_test.js'); let r = new TestResults(); function process_function(decl, body) { let tc = new TestCase(); tc.runTest = function () { let assign_c = 0; let names = {}; for each (let bodyItem in body) for each (let stmtItem in bodyItem.statements) { if (stmtItem.assign) ++assign_c; names[stmtItem.name] = true; } tc.assertEquals(assign_c, 4); tc.assertTrue(names['prepp']); tc.assertTrue(names['premm']); tc.assertTrue(names['postpp']); tc.assertTrue(names['postmm']); }; tc.run(r); } function input_end() { r.verifyExpectedTestsRun(1) } dehydra-89ed48e70997/test/test_include.js0000644000000000000000000000131511757635752020165 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } include('unit_test.js'); function IncludeTestCase(filename) { this.filename = filename; } IncludeTestCase.prototype = new TestCase(); IncludeTestCase.prototype.toString = function() { return "IncludeTestCase(" + this.filename + ")"; } IncludeTestCase.prototype.runTest = function() { try { include(this.filename); } catch (e) { // This is exactly what we want. return; } this.fail("no include error"); }; let names = ['nofile.js', 'syntax_error.js', 'semantic_error.js' ]; let r = new TestResults(); for each (let name in names) { let t = new IncludeTestCase(name); t.run(r); } r.list();dehydra-89ed48e70997/test/test_include2.js0000644000000000000000000000106611757635752020252 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } const GUARD_AGAINST_MULTIPLE_INCLUSIONS = 0 var foo = {} include ("test_include2_lib.js", foo) if (foo.includeCounter !== 1) { print (foo) throw new Error("Failed to do namespace include") } include ("test_include2_lib.js", foo) if (foo.includeCounter !== 1) throw new Error("Double inclusion shouldn't be possible") include ("test_include2_lib.js" ) if (includeCounter !== 1) throw new Error("Failed to do include") include("test_include2.js") print("OK") dehydra-89ed48e70997/test/test_include2_lib.js0000644000000000000000000000016311757635752021075 0ustar rootroot00000000000000var includeCounter if (!includeCounter) includeCounter = 0 includeCounter++ print ("included " + includeCounter) dehydra-89ed48e70997/test/test_integer_types.js0000644000000000000000000000037711757635752021432 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_good.cc', 'output': 'unit_test' } var OK = false function process_tree() { OK = sys.treehydra.gcc.integer_types[itk_char.value].tree_code() == INTEGER_TYPE } function input_end() { if (OK) print("OK") } dehydra-89ed48e70997/test/test_intlit.js0000644000000000000000000000175611757635752020056 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'intlit.cc', 'output': 'unit_test' } // Test that integer literals are handled correctly. include('unit_test.js'); let r = new TestResults(); // v comes from the intlit.cc body[] values, exp comes from our // expected[] values function MyTestCase(v, exp) { this.v = v; this.exp = exp; } MyTestCase.prototype = new TestCase(); MyTestCase.prototype.runTest = function() { // print(this.v[0]); this.assertEquals(this.v[0].value, this.exp); }; let expected = [ '1u', '4294967295u', '-10', '12', '0l', '0ll', '0u', '0ul', '0ull', '-1073741782', '-1073741782l', '-1073741782ll', '1073741866', '1073741866l', '1073741866ll', '-281474976710665ll', '281474976710665ll', '9223372036854775885ull' ]; function process_function(decl, body) { for (let i = 0; i < expected.length; ++i) { new MyTestCase(body[i].statements[0].assign, expected[i]).run(r); } } function input_end() { r.verifyExpectedTestsRun(expected.length) } dehydra-89ed48e70997/test/test_ipa.js0000644000000000000000000000075311757635752017320 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_good.cc', 'output': 'unit_test' } require({ after_gcc_pass: 'einline' }); ls = ["good1", "good2"] function process_tree() { //only go into functions with bodies for (var f = sys.treehydra.gcc.cgraph_nodes; f; f = f.next) if (DECL_STRUCT_FUNCTION(f.decl)) ls.splice(ls.indexOf(decl_name(f.decl)), 1) } function input_end() { if (ls.length) error("Did not see functions I wanted in the input file"); else print('OK') } dehydra-89ed48e70997/test/test_isExtern.js0000644000000000000000000000222511757635752020344 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'isExtern.cc', 'output': 'unit_test', 'lang': 'c++'} // Test for correct results for isExtern include('unit_test.js'); let r = new TestResults(); function TestExtern(type, isExtern) { this.name = type.name + ".isExtern"; this.type = type; this.isExtern = isExtern; } TestExtern.prototype = new TestCase(); TestExtern.prototype.runTest = function() { this.assertEquals(this.type.isExtern, this.isExtern); }; function getMember(t, mname) { let test = t.name + '::' + mname; for each (let m in t.members) { if (m.name.indexOf(test) != -1) return m; } throw Error("Member " + test + " not found."); } let tests = [['a', undefined], ['f', true], ['g', undefined]]; function process_type(t) { if (t.name == 'A') { for each (let [name, value] in tests) new TestExtern(getMember(t, name), value).run(r); } } function process_decl(d) { if (d.name == 'A::b') new TestExtern(d, true).run(r); else if (d.name == 'A::c') new TestExtern(d, undefined).run(r); } function input_end() { let testCount = tests.length + 2; r.verifyExpectedTestsRun(testCount); } dehydra-89ed48e70997/test/test_iterate_vars.js0000644000000000000000000000052611757635752021235 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'iterate_vars.cc', 'output': 'unit_test' } let i = 0; function process_function(decl, statements) { if (decl.name != "foo()") return; for each (let v in iterate_vars(statements)) i++; } function input_end() { if (i != 6) throw new Error("iterate_vars is busted!"); else _print("OK"); } dehydra-89ed48e70997/test/test_lang_type.js0000644000000000000000000000207611757635752020531 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'templ.cc', 'output': 'unit_test', 'lang': 'c++' } // test lang_type union stuff require({werror: true }); include('unit_test.js'); const assertEquals = TestCase.prototype.assertEquals function process_cp_pre_genericize(fndecl) { var counter = 0; function checker (t, depth) { if (t.tree_code() != PARM_DECL) return // code loosely based on dehydra_attachTemplateStuff const record_type = TREE_TYPE(TREE_TYPE(t)) var tpl = CLASSTYPE_TI_TEMPLATE(record_type) while (DECL_TEMPLATE_INFO (tpl)) tpl = DECL_TI_TEMPLATE (tpl) const name = DECL_NAME (tpl) assertEquals (IDENTIFIER_POINTER (name), "templ") const info = TYPE_TEMPLATE_INFO (record_type); const args = TI_ARGS (info) if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args)) args = TREE_VEC_ELT (args, TREE_VEC_LENGTH (args) - 1); assertEquals(TREE_VEC_LENGTH (args), 1) const arg = TREE_VEC_ELT (args, 0) assertEquals (decl_name(TYPE_NAME( arg)), "Foo") print("OK") } walk_tree (DECL_SAVED_TREE (fndecl), checker, new Map()) } dehydra-89ed48e70997/test/test_lazy_atts.js0000644000000000000000000000035311757635752020555 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'lazy_atts.cc', 'output': 'unit_test' } include('gcc_util.js'); function process_tree(fndecl) { let aa = TYPE_ATTRIBUTES(TREE_TYPE(fndecl)); let ac = translate_attributes(aa); print("OK"); } dehydra-89ed48e70997/test/test_lazytypes.js-failing0000644000000000000000000000626511757635752022226 0ustar rootroot00000000000000// {'test': 'treehydra', 'input': 'types.cc', 'output': 'unit_test' } // Test that the output of lazy_types is the same as the output of // dehydra. We rely on the behavior that process_tree_type is always called // immediately after the matching process_type include('unit_test.js'); include('unstable/lazy_types.js'); function ValueCheck(v1, v2, path, message) { if (v1 != v2) throw new Error(message + "\nat path: " + path + " dehydra: " + v1 + " lazy: " + v2); } function BigIntCheck(v1, v2, path, message) { if (bigint_cmp(v1, v2) != 0) throw new Error(message + "\nat path: " + path + " dehydra: " + v1 + " lazy: " + v2); } function JSMatchesTestCase(d, lazy) { this._d = d; this._lazy = lazy; this.name = "JSMatchesTestCase<" + d.name + ">"; } JSMatchesTestCase.prototype = new TestCase(); JSMatchesTestCase.prototype.runTest = function() { let m = new Map(); let t = this; function check(v1, v2, path) { /* sometimes dehydra returns things as strings which lazytypes always makes * into numbers... */ if (typeof v1 == 'string' && v2 instanceof BigInt) { let s = (/^[-\d]+/(v1))[0]; v1 = new BigInt(s); } if (typeof v1 == 'string' && typeof v2 == 'number') v1 = Number(/^[-\d+]+/(v1)); ValueCheck(typeof v1, typeof v2, path, "types don't match"); if ((v1 instanceof BigInt) && (v2 instanceof BigInt)) { BigIntCheck(v1, v2, path, "BigInts don't match"); return; } switch (typeof v1) { case "undefined": return; case "number": case "string": case "boolean": ValueCheck(v1, v2, path, "values don't match"); return; case "object": if (v1 instanceof Array) check_array(v1, v2, path); else check_object(v1, v2, path); return; default: throw Error("unexpected typeof: " + typeof v1 + ": " + v1); } } function check_array(a1, a2, path) { if (!(a2 instanceof Array)) throw Error("treehydra isn't an array: " + a2); ValueCheck(a1.length, a2.length, path, "array lengths don't match"); if (m.has(a1)) return; m.put(a1); for (let i = 0; i < a1.length; ++i) check(a1[i], a2[i], path + "." + i); } function check_object(o1, o2, path) { if (m.has(o1)) return; m.put(o1); // we don't check locations because definitions and declarations end up oddly in treehydra for (let p in o1) if (p != 'loc' && o1.hasOwnProperty(p)) { check(o1[p], o2[p], path + "." + p); } } try { check(this._d, this._lazy, "root"); } catch (e) { // error(e + "\n" + e.stack); throw e; } } var r = new TestResults(); var gSavedType; function process_type(t) { gSavedType = t; } function process_tree_type(t) { new JSMatchesTestCase(gSavedType, dehydra_convert(t)).run(r); } var gSavedDecl; function process_decl(d) { gSavedDecl = d; } function process_tree_decl(d) { new JSMatchesTestCase(gSavedDecl, dehydra_convert(d)).run(r); } function input_end() { if (r.testsRun < 1) print("Error: not a single test was run!"); r.list(); } dehydra-89ed48e70997/test/test_loadlib.js0000644000000000000000000000107711757635752020155 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } // Test that treehydra.js is loaded before this file, and that this // file is loaded only once. if (this.LOADED_BEFORE) { print("Err: loaded before"); } else { LOADED_BEFORE = true; if (TREE_CODE == undefined) { print("Err: treehydra.js not loaded"); } else if (sameVar == undefined) { // Test that Dehydra lib is loaded too. This only applies as long // as treehydra contains dehydra. print("Err: system.js not loaded"); } else { print("OK"); } } dehydra-89ed48e70997/test/test_location.js0000644000000000000000000000162011757635752020351 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'onefunc.cc', 'output': 'unit_test' } include('treehydra.js'); include('unit_test.js'); //include('/home/dmandelin/treehydra-analysis/util.js') function process_cp_pre_genericize(fndecl) { let r = new TestResults(); new LocationTestCase(fndecl).run(r); r.list(); } function LocationTestCase(fndecl) { this.fndecl = fndecl; } LocationTestCase.prototype = new TestCase(); LocationTestCase.prototype.runTest = function() { let body = fn_decl_body(this.fndecl); for (let i = tsi_start (body); !i.end (i); i.next()) { let stmt = i.stmt(); if (stmt.gstmt) { let loc = stmt.gstmt.locus; this.assertEquals(loc_is_valid(loc), true); } } } function loc_is_valid(loc) { return loc != undefined && typeof loc.file == 'string' && typeof loc.line == 'number' && typeof loc.column == 'number' && loc.toString().split(":").length == 3; } dehydra-89ed48e70997/test/test_locks_bad1.js0000644000000000000000000000023711757635752020546 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_bad1.cc', 'output': 'stderr_has_re("^locks_bad1.cc:11:(12:)? error: precondition not met")' } include("esp_lock.js") dehydra-89ed48e70997/test/test_locks_bad2.js0000644000000000000000000000023711757635752020547 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_bad2.cc', 'output': 'stderr_has_re("^locks_bad2.cc:12:(14:)? error: precondition not met")' } include("esp_lock.js") dehydra-89ed48e70997/test/test_locks_bad3.js0000644000000000000000000000023711757635752020550 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_bad3.cc', 'output': 'stderr_has_re("^locks_bad3.cc:10:(14:)? error: precondition not met")' } include("esp_lock.js") dehydra-89ed48e70997/test/test_locks_bad4.js0000644000000000000000000000023711757635752020551 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_bad4.cc', 'output': 'stderr_has_re("^locks_bad4.cc:13:(12:)? error: precondition not met")' } include("esp_lock.js") dehydra-89ed48e70997/test/test_locks_good.js0000644000000000000000000000014611757635752020666 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_good.cc', 'output': 'stderr_empty' } include("esp_lock.js") dehydra-89ed48e70997/test/test_locks_good2.js0000644000000000000000000000014711757635752020751 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'locks_good2.cc', 'output': 'stderr_empty' } include("esp_lock.js") dehydra-89ed48e70997/test/test_map.js0000644000000000000000000000114311757635752017316 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } include("map.js") function assert(t) { if (!t) throw new Error("Misbehaving map") } function test_map(map_constructor) { let m = new map_constructor(); m.put('a', 1); m.put('b', 2); m.put('c', 3); assert(m.has('a')) assert(m.has('b')) assert(m.has('c')) m.remove('b'); m.remove('c'); m.put('c', 5); m.put('c', 7); assert(m.get('c') == 7) m.put(this, 11) assert(m.get(this) == 11) assert(!m.remove({})) assert(m.remove(this)) } test_map (Map) test_map (InjHashMap) print("OK") dehydra-89ed48e70997/test/test_members.js0000644000000000000000000000162411757635752020177 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'members.cc', 'output': 'unit_test', 'lang': 'c++' } include('unit_test.js'); let classes = {'Outer': null, 'Outer::Inner': 'Outer', 'Outer::Inner::MostInner': 'Outer::Inner'}; function MemberTestCase(type, inner) { this.type = type; this.inner = inner; this.name = this.type.name; } MemberTestCase.prototype = new TestCase(); MemberTestCase.prototype.runTest = function() { print("Testing type: " + this.type.name); if (!this.inner) this.assertTrue(this.type.memberOf === undefined); else this.assertEquals(this.type.memberOf.name, this.inner); for each (let member in this.type.members) this.assertTrue(member.memberOf === this.type); }; let r = new TestResults(); function process_type(type) { if (classes.hasOwnProperty(type.name)) new MemberTestCase(type, classes[type.name]).run(r); } function input_end() { r.list(); } dehydra-89ed48e70997/test/test_numinfo.js0000644000000000000000000000535011757635752020220 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'numinfo.cc', 'output': 'unit_test' } include('unit_test.js') let r = new TestResults(); function NuminfoTestCase(type, unsigned, prec, min, max) { this.type = type; this.unsigned = unsigned; this.prec = prec; this.min = min; this.max = max; } NuminfoTestCase.prototype = new TestCase(); NuminfoTestCase.prototype.runTest = function () { /*deals with gcc using ll suffix on 32bit*/ function llcompare(val, expected) { let r= new RegExp("^"+expected+"(ll?)?$"); ret = r.test(val); if (!ret) { error("Expected '"+expected+"', got '"+val+"'"); } return ret; } let type = this.type; if (this.unsigned) this.assertEquals(type.isUnsigned, true); else this.assertEquals(type.isSigned, true); this.assertEquals(type.precision, this.prec); this.assertTrue(llcompare(type['min'] ? type.min.value : type['min'], this.min)); this.assertTrue(llcompare(type['max'] ? type.max.value : type['max'], this.max)); } const expected = { 'int8var': [ false, 8, '-128', '127' ], 'int16var': [ false, 16, '-32768', '32767' ], 'int32var': [ false, 32, '-2147483648', '2147483647' ], 'int64var': [ false, 64, '-9223372036854775808', '9223372036854775807' ], 'uint8var': [ true, 8, '0u', '255u' ], 'uint16var': [ true, 16, '0u', '65535u' ], 'uint32var': [ true, 32, '0u', '4294967295u' ], 'uint64var': [ true, 64, '0u', '18446744073709551615u' ], 'floatvar': [ false, 32, undefined, undefined ], 'doublevar': [ false, 64, undefined, undefined ], 'boolvar': [ true, 1, '0u', '1u' ] } let tested = {} function process_decl(v) { const e = expected[v.name] if (e) { tested[v.name] = true; new NuminfoTestCase(v.type.typedef || v.type, e[0], e[1], e[2], e[3]).run(r); } else if (['enumvar', 'refvar', 'pointervar'].indexOf(v.name) >= 0) { // only test for relevant things here tested[v.name] = true; let tc = new TestCase(); tc.runTest = function () { tc.assertTrue((v.name != 'enumvar' || v.type.isUnsigned == true) && v.type.precision > 0); } tc.run(r); } } function input_end() { if (r.testsRun != 14) { print('Error: must be 14 tests run, instead ran ' + r.testsRun); } else { expected['enumvar'] = expected['refvar'] = expected['pointervar'] = true; for (let v in expected) if (!tested[v]) { print('Error: variable ' + v + ' was left untested'); return; } r.list(); } } dehydra-89ed48e70997/test/test_parameters.js0000644000000000000000000000106411757635752020706 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'parameters.cc', 'output': 'unit_test' } // Test that function parameters are exposed in the .parameters array include('unit_test.js'); function DeclTestCase(d) { this.decl = d; } DeclTestCase.prototype = new TestCase(); DeclTestCase.prototype.runTest = function() { this.assertEquals(this.decl.parameters[0].name, 'i'); this.assertEquals(this.decl.parameters[1].name, 'j'); }; let r = new TestResults(); function process_decl(d) { new DeclTestCase(d).run(r); } function input_end() { r.verifyExpectedTestsRun(1) } dehydra-89ed48e70997/test/test_print_loc.js0000644000000000000000000000037111757635752020534 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'members.cc', 'output': 'stdout_has_re("^members\.cc:10:(7:)? Outer::a$")', 'lang': 'c++' } function process_type(t) { if (t.name != "Outer") return for each (var m in t.members) { print(m.name, m.loc) } } dehydra-89ed48e70997/test/test_process_decl.js0000644000000000000000000000154311757635752021212 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'decl.cc', 'output': 'unit_test', 'lang': 'c++' } let assert_passes = 0; function xassert (t) { if (!t) throw new Error("process_decl isn't working right"); assert_passes++; } function process_decl(v) { if (v.name == "plain_var") xassert(v.type.name == "int") else if (v.name == "some_typedef") xassert(v.type.typedef) else if (v.name == "FooTemplate") { // normal template decl info xassert(v.type.template.arguments.length) } else if (v.name == "FooTemplate" || v.name == "FooTemplate") xassert (v.type.template.arguments.length == 2) else if (v.name == "forward_func_decl()") xassert(v.isFunction) else print(v) } function input_end() { if (assert_passes == 5) print("OK") else print("FAIL: expected 5 decls, got " + assert_passes); } dehydra-89ed48e70997/test/test_process_tree_type.js0000644000000000000000000000033211757635752022276 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'assign.cc', 'output': 'unit_test', 'lang': 'c++' } function process_tree_type(type) { // check if we hit class "book" if (decl_name(TYPE_NAME(type)) == "book") print("OK") } dehydra-89ed48e70997/test/test_process_tree_type2.js0000644000000000000000000000025611757635752022365 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'typedef_gtk2xtbin_reduced.c', 'output': 'unit_test', 'lang': 'c', 'version': '4.5' } function process_tree_type(type) { print('OK'); } dehydra-89ed48e70997/test/test_require.js0000644000000000000000000000224411757635752020220 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } include('unit_test.js'); function RequireTestCase() { } RequireTestCase.prototype = new TestCase(); RequireTestCase.prototype.testBadKey = function() { // Fails with a warning -- should look for that, but not doing it now. require({ a: 7 }); } RequireTestCase.prototype.testVersion = function() { let opts = require({ version: '1.7' }); let r = [ 'a' for (i in [ 1, 2, 3 ]) ]; this.assertEquals(opts.version, '1.7'); } RequireTestCase.prototype.testEmpty = function() { require({ }); } RequireTestCase.prototype.testStrict1 = function() { let opts = require({ strict: true }); this.assertEquals(opts.strict, true); require({ strict: false }); } RequireTestCase.prototype.testStrict2 = function() { let opts = require({ strict: true, werror: true }); this.assertEquals(opts.strict, true); this.assertEquals(opts.werror, true); let ok = false; try { globalsNotAllowed = 9; } catch (e) { ok = true; } this.assertTrue(ok); require({ strict: false, werror: false }); } let r = new TestResults(); new RequireTestCase().runTestCases(r); r.list();dehydra-89ed48e70997/test/test_return_expr.js0000644000000000000000000000136511757635752021124 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'return_expr.cc', 'output': 'unit_test', 'lang': 'c,c++' } require({ after_gcc_pass: "cfg" }); include('unit_test.js'); var test = new TestCase(); function process_tree(fn) { let cfg = function_decl_cfg(fn); for (let bb in cfg_bb_iterator(cfg)) { for (let isn in bb_isn_iterator(bb)) { if (isn.tree_code() == GIMPLE_RETURN) { let ret = return_expr(isn); test.assertTrue(ret.tree_code() == VAR_DECL); } } } } function process_cp_pre_genericize(fn) { walk_tree(DECL_SAVED_TREE(fn), function (t) { if (t.tree_code() == RETURN_EXPR) { let ret = return_expr(t); test.assertTrue(ret.tree_code() == VAR_DECL); } }); } function input_end() { print("OK") } dehydra-89ed48e70997/test/test_scopes.js0000644000000000000000000000135311757635752020040 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'scopes.cc', 'output': 'unit_test', 'lang': 'c++' } // Test that scoping operators appear in type names as expected. include('unit_test.js'); let expected_names = 'NS::Outer::Inner,NS::Outer,Outer2::Inner2,Outer2'; let check = {}; for each (let name in expected_names.split(',')) { check[name] = true; } let r = new TestResults(); // Test is constructed so all function types should have 2 args. function ScopeTestCase(type) { this.type = type; } ScopeTestCase.prototype = new TestCase(); ScopeTestCase.prototype.runTest = function() { this.assertTrue(check[this.type.name]); } function process_type(type) { new ScopeTestCase(type).run(r); } function input_end() { r.verifyExpectedTestsRun(4) } dehydra-89ed48e70997/test/test_shortname.js0000644000000000000000000000022111757635752020535 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'onefunc.cc', 'output': 'unit_test' } function process_decl(f) { if (f.shortName == "foo") print("OK") } dehydra-89ed48e70997/test/test_skipthis.js0000644000000000000000000000137711757635752020410 0ustar rootroot00000000000000include('unit_test.js'); let r = new TestResults(); // Test is constructed so all function types should have 2 args. function SkipThisTestCase(fntype) { this.fntype = fntype; } SkipThisTestCase.prototype = new TestCase(); SkipThisTestCase.prototype.runTest = function() { this.assertEquals(this.fntype.parameters.length, 2); } function process_function(decl, body) { let rtype = decl.type.type; // The class type case let members = rtype.members; if (members != undefined) { for each (let m in members) { new SkipThisTestCase(m.type).run(r); } } // The fptr typedef case let type = rtype.typedef; if (type != undefined) { new SkipThisTestCase(type.type).run(r); } } function input_end() { r.verifyExpectedTestsRun(3) } dehydra-89ed48e70997/test/test_static_func.cc0000644000000000000000000000024411757635752021015 0ustar rootroot00000000000000struct X { static void st() {} void dy() {} virtual void vi() {} virtual void pvi() = 0; }; static void gst() {} void gdy() {} namespace { void nst() {} } dehydra-89ed48e70997/test/test_static_func.js0000644000000000000000000000072311757635752021046 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'test_static_func.cc', 'output': 'unit_test', 'lang': 'c++' } function assert (t) { if (!t) throw new Error("static isn't correct"); } function process_function(f) { print(f.name + f.isStatic) switch (f.name) { case "X::st()": case "gst()": case "::nst()": case "{anonymous}::nst()": assert(f.isStatic) break; default: assert(!f.isStatic) } } function input_end() { print("OK"); } dehydra-89ed48e70997/test/test_strict.js0000644000000000000000000000044611757635752020056 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'onefunc.cc', 'output': 'unit_test' } // This is just to make sure the libs can load in strict mode. require({ strict: true, werror: true }); function process_tree(fndecl) { print("OK"); } function process_function(decl, body) { print("OK"); }dehydra-89ed48e70997/test/test_stringlit.js0000644000000000000000000000104311757635752020557 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'stringlit.cc', 'output': 'unit_test' } var OK = true; var expected = [ 'a-test', 'b-te\0st', 'c-test', 'd-te\0st' ]; function process_cp_pre_genericize(fndecl) { function checker (t) { if (TREE_CODE (t) != STRING_CST) return true; var s = TREE_STRING_POINTER(t); //print (s.replace(/\0/g, "\\0")); if (s != expected.shift()) OK = false; }; walk_tree (DECL_SAVED_TREE (fndecl), checker); } function input_end() { if (OK && expected.length == 0) print("OK"); } dehydra-89ed48e70997/test/test_sys_gcc_info.js0000644000000000000000000000112111757635752021202 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c,c++' } include('unit_test.js'); require({strict:true}) function SysTest(v1, v2) { } SysTest.prototype = new TestCase(); SysTest.prototype.runTest = function() { // aux_base_name has the filename passed to gcc -extension // aux_base_name isn't useful when running via g++(as opposed to cc1plus) // this.assertEquals(sys.aux_base_name, "empty"); this.assertEquals(sys.main_input_filename, "empty.cc"); } function input_end() { var r = new TestResults() new SysTest().run(r); r.list(); } dehydra-89ed48e70997/test/test_templ.js0000644000000000000000000000027711757635752017671 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'access9.cc', 'output': 'unit_test', 'lang': 'c++' } // crazy template args in access9.cc made things crash before function process_class() { print("OK") } dehydra-89ed48e70997/test/test_template_function.js0000644000000000000000000000033411757635752022262 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'templ-func3.cc', 'output': 'unit_test', 'lang': 'c++' } function process_function(f) { if (f.name == "TemplateFunction(A) [with A = int]") print('OK'); else print(f.name) } dehydra-89ed48e70997/test/test_template_member.js0000644000000000000000000000206111757635752021703 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'template_member.cc', 'output': 'unit_test', 'lang': 'c++' } // Test that templated function declarations are annotated usefully include('unit_test.js'); let r = new TestResults(); function TemplateTestCase(type) { this.type = type; } TemplateTestCase.prototype = new TestCase(); TemplateTestCase.prototype.runTest = function() { let templatemember = this.type.members[0]; this.assertEquals(templatemember.template[0].name, "B"); this.assertEquals(templatemember.template[0].type.name, "B"); this.assertTrue(templatemember.template[0].type.isTypename); this.assertTrue(templatemember.type.parameters[0].isTypename); this.assertEquals(templatemember.template[0].type, templatemember.type.parameters[0]); templatemember = this.type.members[1]; this.assertEquals(templatemember.template[0].name, "I"); this.assertEquals(templatemember.template[0].type.name, "int"); } function process_type(type) { new TemplateTestCase(type).run(r); } function input_end() { r.verifyExpectedTestsRun(1) } dehydra-89ed48e70997/test/test_treehydra_debug.js0000644000000000000000000000035311757635752021700 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'assign.cc', 'output': 'unit_test' } require({treehydra_debug:true}) function process_tree(d) { if (d.hasOwnProperty("SEQUENCE_N") && d.base.hasOwnProperty("_struct_name")) print("OK") } dehydra-89ed48e70997/test/test_treehydra_decl_as_string.js0000644000000000000000000000025211757635752023570 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'assign.cc', 'output': 'unit_test' } function process_tree(d) { if (d._decl_as_string.str.indexOf("foo()") != -1) print("OK") } dehydra-89ed48e70997/test/test_treehydra_dehydra_types.js0000644000000000000000000000155711757635752023465 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'access.cc', 'output': 'unit_test', 'lang': 'c++' } if (this.TREE_CODE) include('unstable/lazy_types.js'); var OK = false function assert (t) { if (!t) throw new Error("visibility is busted"); } function process_type(t) { if (t.name != "B") return print("class " + t.name + " : " + [b.access + " " + b.type.name for each (b in t.bases)].join(",")) for each (let m in t.members) { print(m.access + " " + m.name) } assert(t.bases[0].access == "public") OK = true } function process_tree(fn) { if (decl_name(fn) != "getB") return let ttype = TREE_TYPE(fn) let lazyB = dehydra_convert(ttype).type.type print("--lazy---"); process_type(lazyB) } function input_end() { print("OK") } function process_decl(v) { if (v.name == "A::static_member") assert(v.access == "private") } dehydra-89ed48e70997/test/test_treehydra_type_as_string.cc0000644000000000000000000000073511757635752023621 0ustar rootroot00000000000000// test pointer-to-member-fn and pointer-to-member struct s1 { void m1(int); int i; }; typedef int t1; typedef bool t2; typedef long double t3; typedef const long int t4; typedef s1 t5; typedef const s1* const & t6; typedef const int __attribute__ ((__vector_size__ (8))) t7; typedef long double __complex__ const t8; typedef volatile unsigned short* const * t9[]; typedef t9* const & t10; typedef void (s1::*t11)(int); typedef t11 (s1::*t12)(int); typedef int s1::*t13; dehydra-89ed48e70997/test/test_treehydra_type_as_string.js0000644000000000000000000000242011757635752023641 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'test_treehydra_type_as_string.cc', 'output': 'unit_test' } require({ after_gcc_pass: "cfg" }); include('unstable/dehydra_types.js'); let tests = { "t1": "int", "t2": "bool", "t3": "long double", "t4": "const long int", "t5": "s1", "t6": "const s1* const&", "t7": "const vector int[2]", "t8": "const complex long double", "t9": "volatile short unsigned int* const*[]", "t10": "t9* const&", "t11": "void (s1::*)(s1*, int)", "t12": "t11 (s1::*)(s1*, int)", "t13": "int s1::*" }; function process_tree_type(t) { // pull the name of the typedef let tn = TYPE_NAME(t); if (!tn || TREE_CODE(tn) != TYPE_DECL) return; let name = decl_as_string(tn); // pull the original type t = DECL_ORIGINAL_TYPE(tn); if (!t) return; // look up the expected result let expected = tests[name]; if (!expected) throw("couldn't find test"); let result = type_as_string(t); // print(' "' + name + '": "' + result + '",'); if (expected != result) throw new Error("Incorrect type for " + name + "; expected " + expected + ", got " + result) delete tests[name]; print("OK"); } function input_end() { let i = 0; for each (let t in tests) ++i; if (i != 0) throw new Error("missed tests " + tests); } dehydra-89ed48e70997/test/test_type_decl_distinction.js0000644000000000000000000000047111757635752023123 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'numinfo.cc', 'output': 'unit_test' } include('unit_test.js') let ok = true function process_type(t) { ok = ok && t.constructor == DehydraType for each (let m in t.members) { ok = ok && m instanceof DehydraDecl } } function input_end() { if (ok) print("OK") } dehydra-89ed48e70997/test/test_typedef_aligned.js0000644000000000000000000000077011757635752021671 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'typedef_aligned.cc', 'output': 'unit_test', 'version': '4.5' } include('unit_test.js'); include('util.js'); let r = new TestResults(); TypedefTestCase.prototype = new TestCase(); function TypedefTestCase(type) { this.type = type; } TypedefTestCase.prototype.runTest = function () { let type = this.type; this.assertEquals(type.name, "Blah"); } function process_type(t) { while(t) { new TypedefTestCase(t).run(r); r.list(); t = t.typedef; } } dehydra-89ed48e70997/test/test_typedef_const.js0000644000000000000000000000105311757635752021407 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'typedef.cc', 'output': 'unit_test', 'lang': 'c++' } // Make sure that 'const' is handled correctly on typedefs include('unit_test.js'); let r = new TestResults(); function ConstTypedefTest(type) { this.type = type; } ConstTypedefTest.prototype = new TestCase(); ConstTypedefTest.prototype.runTest = function() { this.assertTrue(this.type.isConst); }; function process_decl(d) { if (d.name == 'const_PRInt32') new ConstTypedefTest(d.type).run(r); } function input_end() { r.verifyExpectedTestsRun(1) } dehydra-89ed48e70997/test/test_typedefs.js0000644000000000000000000000472211757635752020372 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'typedef.cc', 'output': 'unit_test', 'lang': 'c,c++' } include('unit_test.js'); include('unstable/lazy_types.js'); function TypedefCheck(name, check) { this.name = 'TypedefCheck<' + name + '>'; this.check = check; } TypedefCheck.prototype = new TestCase(); TypedefCheck.prototype.dehydra = null; TypedefCheck.prototype.treehydra = null; TypedefCheck.prototype.runTest = function() { this.assertTrue(this.dehydra); this.check(this.dehydra); this.assertTrue(this.treehydra); this.check(this.treehydra); }; const tests = { 'PRUint32': new TypedefCheck('PRUint32', function(got) { this.assertEquals(got.typedef.name, 'unsigned int'); }), 'PRInt32': new TypedefCheck('PRInt32', function(got) { this.assertEquals(got.typedef.name, 'int'); }), 'PRInt32_2': new TypedefCheck('PRInt32_2', function(got) { this.assertEquals(got.typedef.name, 'PRInt32'); }), 'PRInt322': new TypedefCheck('PRInt322', function(got) { this.assertTrue(got.isPointer); this.assertTrue(got.typedef.isPointer); this.assertEquals(got.typedef.type.name, 'PRInt32'); }), 'TypedefTypedef': new TypedefCheck('TypedefTypedef', function(got) { this.assertEquals(got.typedef.name, 'PRUint32'); }), 'stype': new TypedefCheck('stype', function(got) { this.assertEquals(got.typedef.name, 's'); }), /** * sizetype isn't even seen by gcc <=4.3 * GCC bug: typedefs decls in class context don't have DECL_ORIGINAL_CONTEXT, * so we don't think they are typedefs. Yuck. */ 'size_type': new TypedefCheck('size_type', function(got) { this.assertEquals(got.typedef.name, 'PRUint32'); }), '__m64': new TypedefCheck('__m64', function(got) {}), }; let r = new TestResults(); function process_type(t) { if (!t.typedef) return; let test = tests[t.name]; if (test === undefined) throw new Error("Unexpected typedef: " + t.name); test.dehydra = t; } function process_tree_type(t) { t = dehydra_convert(t); print("Found treehydra: " + t); if (!t.typedef) return; let test = tests[t.name]; if (test === undefined) throw new Error("Unexpected typedef: " + t.name); test.treehydra = t; test.run(r); } function input_end() { for (let testname in tests) { let t = tests[testname] // warn about typedefs processed by dehydra but not treehydra if (t.dehydra && t.treehydra === null) r.addError(t, "Declaration not processed: " + testname); } r.list(); } dehydra-89ed48e70997/test/test_using_namespace.js0000644000000000000000000000020511757635752021700 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'using_namespace.cc', 'output': 'unit_test', 'lang': 'c++' } function input_end() { print('OK') } dehydra-89ed48e70997/test/test_variadic_templ.js0000644000000000000000000000220311757635752021522 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'var_templ.cc', 'output': 'unit_test', 'lang': 'c++0x' } // Test that variadic templates are handled correctly include('unit_test.js'); let r = new TestResults(); function FuncTestCase(decl) { this.decl = decl; } FuncTestCase.prototype = new TestCase(); FuncTestCase.prototype.runTest = function() { this.assertTrue(this.decl.isFunction); let params = this.decl.type.parameters; this.assertEquals(params.length, 1); this.assertTrue(params[0].isTypename); this.assertEquals(params[0].name, "A ..."); }; function ClassTestCase(type) { this.type = type; } ClassTestCase.prototype = new TestCase(); ClassTestCase.prototype.runTest = function() { this.assertEquals(this.type.name, "C"); this.assertEquals(this.type.template.arguments.length, 1); this.assertTrue(this.type.template.arguments[0].isTypename); this.assertEquals(this.type.template.arguments[0].name, "B ..."); }; function process_decl(decl) { if (decl.name == 'func') new FuncTestCase(decl).run(r); else if (decl.name == 'C') new ClassTestCase(decl.type).run(r); } function input_end() { r.verifyExpectedTestsRun(2) } dehydra-89ed48e70997/test/test_variant.js0000644000000000000000000000114111757635752020203 0ustar rootroot00000000000000// { 'test': 'dehydra', 'input': 'variant.cc', 'output': 'unit_test' } // Make sure that const types (variants) point back to the main variant include('unit_test.js'); let r = new TestResults(); function VariantTest(main, variant) { this.main = main; this.variant = variant; } VariantTest.prototype = new TestCase(); VariantTest.prototype.runTest = function() { this.assertEquals(this.variant.variantOf, this.main); }; function process_decl(d) { print(d); new VariantTest(d.type.parameters[0].type, d.type.parameters[1].type).run(r); } function input_end() { r.verifyExpectedTestsRun(1) } dehydra-89ed48e70997/test/test_virtual_fn_addr_exprs.js0000644000000000000000000000736711757635752023143 0ustar rootroot00000000000000// { 'test': 'treehydra', 'input': 'virtual_fn_addr_exprs.cc', 'output': 'unit_test', 'lang': 'c++0x' } include('unit_test.js'); require({ "after_gcc_pass": "cfg"}); function ExpectedTable(table) { this.table = table; this.index = 0; } let cfg_expected = new ExpectedTable( [ "void a(this Derived* const)", "void a(this Base* const)", "void a(this Derived* const)", "void b(this NotDerived* const, y double)", "void a(this Derived* const)", "void a(this Base* const)", "void b(this NotDerived* const, y double)", "int c(this NotDerived* const)" ] ); let pregen_expected = new ExpectedTable( [ "void a(this Derived* const)", "void a(this Base* const)", "void a(this Derived* const)", "void a(this Base* const)", "void a(this Derived* const)", "void b(this NotDerived* const, y double)", "void a(this Base* const)", "void a(this Derived* const)", "void a(this Derived* const)", "void a(this Base* const)", "double d(this Derived* const, x int)", "void c(this Derived* const)", "void b(this NotDerived* const, y double)" ] ); let localdecls_expected = new ExpectedTable( [ "double d(this Derived* const, x int)", "void c(this Derived* const)", "void a(this Base* const)", "void a(this Derived* const)", "void a(this Derived* const)", "void a(this Base* const)" ] ); let treedecl_expected = new ExpectedTable( [ "void a(this Derived* const)", "void a(this Base* const)", "void a(this Base* const)", "void a(this Derived* const)", "void a(this Base* const)", "void b(this NotDerived* const, y double)" ] ); let r = new TestResults(); function FuncPtrTestCase(func, expected) { this.func = func; this.expected = expected; } FuncPtrTestCase.prototype = new TestCase(); FuncPtrTestCase.prototype.runTest = function () { let func = this.func; let expected = this.expected; let str = expected.table[expected.index++]; if (!str) throw new Error("we've seen too many functions"); this.assertEquals(pretty_func(func), str); } function process_tree(fn) { for (let d in local_decls_iterator(fn)) { let init = DECL_INITIAL(d); if (!init) continue; walk_tree(init, function (t) { for each (let f in resolve_virtual_fn_addr_exprs(t)) { new FuncPtrTestCase(f, localdecls_expected).run(r); r.list(); } }); } let cfg = function_decl_cfg(fn); for (let isn in cfg_isn_iterator(cfg)) { for each (let f in resolve_virtual_fn_addr_exprs(isn)) { new FuncPtrTestCase(f, cfg_expected).run(r); r.list(); } } } function process_cp_pre_genericize(fn) { walk_tree(DECL_SAVED_TREE(fn), function (t) { for each (let f in resolve_virtual_fn_addr_exprs(t)) { new FuncPtrTestCase(f, pregen_expected).run(r); r.list(); } }); } function process_tree_decl(decl) { let init = DECL_INITIAL(decl); if (!init) return; walk_tree(init, function (t) { for each (let f in resolve_virtual_fn_addr_exprs(t)) { new FuncPtrTestCase(f, treedecl_expected).run(r); r.list(); } }); } function pretty_func(fn) { return rfunc_string(rectify_function_decl(fn)); } function input_end() { if (cfg_expected.index != cfg_expected.table.length) throw new Error("didn't see all the functions we expected walking the cfg"); if (pregen_expected.index != pregen_expected.table.length) throw new Error("didn't see all the functions we expected with pre-generic code"); if (localdecls_expected.index != localdecls_expected.table.length) throw new Error("didn't see all the functions we expected in local declarations"); if (treedecl_expected.index != treedecl_expected.table.length) throw new Error("didn't see all the functions we expected in global declarations"); } dehydra-89ed48e70997/test/test_virtual_inheritance.js0000644000000000000000000000152111757635752022600 0ustar rootroot00000000000000// { 'test': 'dehydra,treehydra', 'input': 'virtual_inheritance.cc', 'output': 'unit_test', 'lang': 'c++' } if (this.TREE_CODE) include('unstable/lazy_types.js'); var OK = false function assert (t) { if (!t) throw new Error("visibility is busted"); } function process_type(t) { if (t.name != "C") return print("class " + t.name + " : " + [b.access + " " + (b.isVirtual? "virtual ": "") + b.type.name for each (b in t.bases)].join(",")) print("t.bases[0].isVirtual: " + t.bases[0].isVirtual); assert(! t.bases[0].isVirtual); assert(t.bases[1].isVirtual); OK = true } function process_tree(fn) { if (decl_name(fn) != "getC") return let ttype = TREE_TYPE(fn) let lazyC = dehydra_convert(ttype).type.type print("--lazy---"); process_type(lazyC) } function input_end() { print("OK") } dehydra-89ed48e70997/test/tree.js0000644000000000000000000000037211757635752016444 0ustar rootroot00000000000000include ("treehydra.js") function process_tree(function_decl) { print (decl_name (function_decl) + "()") var b = DECL_SAVED_TREE (function_decl) sanity_check (b) pretty_walk (b) } function user_print(value) { print("user_print:"+value) } dehydra-89ed48e70997/test/typedef.cc0000644000000000000000000000071111757635752017113 0ustar rootroot00000000000000typedef unsigned int PRUint32; typedef int PRInt32; typedef PRInt32 PRInt32_2; typedef PRInt32* PRInt322; typedef PRUint32 TypedefTypedef; class s { typedef PRUint32 size_type; size_type foo; PRUint32 pru32; PRInt322 i; TypedefTypedef tt; }; typedef s stype; typedef long long __m64 __attribute__ ((__vector_size__ (8), __may_alias__)); const PRInt32 const_PRInt32 = 1; static __inline __m64 _mm_setzero_si64 (void) { return (__m64)0LL; } dehydra-89ed48e70997/test/typedef_aligned.cc0000644000000000000000000000006711757635752020602 0ustar rootroot00000000000000typedef struct { } Blah __attribute__ ((__aligned__)); dehydra-89ed48e70997/test/typedef_gtk2xtbin_reduced.c0000644000000000000000000000252111757635752022440 0ustar rootroot00000000000000// Reduced from mozilla-central/widget/src/gtkxtbin/gtk2xtbin.c typedef struct _GError GError; struct _GError { }; typedef struct _GObjectClass GObjectClass; struct _GObjectClass { }; typedef struct _GOutputStream GOutputStream; typedef struct _GOutputStreamClass GOutputStreamClass; struct _GOutputStream { }; struct _GOutputStreamClass { GObjectClass parent_class; void (* write_fn) (GOutputStream *stream, GError **error); void (* splice) (GOutputStream *stream, GError **error); void (* flush) (GOutputStream *stream, GError **error); void (* close_fn) (GOutputStream *stream, void* user_data); void (* flush_finish) (GOutputStream *stream, GError **error); void (* close_async) (GOutputStream *stream, void* user_data); void (* close_finish) (GOutputStream *stream, GError **error); void (*_g_reserved1) (void); void (*_g_reserved2) (void); void (*_g_reserved3) (void); void (*_g_reserved4) (void); void (*_g_reserved5) (void); void (*_g_reserved6) (void); void (*_g_reserved7) (void); void (*_g_reserved8) (void); }; struct _GFilterOutputStream { GOutputStreamClass parent_class; }; dehydra-89ed48e70997/test/types.cc0000644000000000000000000000060011757635752016614 0ustar rootroot00000000000000union UnionNode; typedef union UnionNode Union; struct Boo; void func(struct Boo*) { } struct Boo { unsigned int i:31; } foo; typedef struct { int mem; } TypedefedStruct; TypedefedStruct s; void func2(struct Boo*b) { b->i; } union UnionNode { struct Boo boo; }; static Union global_namespace; static int array[10]; class Foo; volatile const Foo * c; long int long_var; dehydra-89ed48e70997/test/unit_test.js0000644000000000000000000000445011757635752017524 0ustar rootroot00000000000000/** Unit test framework for JS */ function TestResults() { this.testsRun = 0; this.errors = new Array(); this.failures = new Array(); } TestResults.prototype.startTest = function (test) { ++this.testsRun; }; TestResults.prototype.addError = function (test, exc) { this.errors.push([test, exc]); }; TestResults.prototype.addFailure = function (test, exc) { this.failure.push([test, exc]); }; TestResults.prototype.list = function () { if (this.errors.length == 0 && this.failures.length == 0) { print("OK: " + this.testsRun + " tests passed"); } else { for each (let errors in [ this.errors, this.failures ]) { for (let i = 0; i < errors.length; ++i) { let [t, e] = errors[i]; print("ERR " + t + " " + e + "\n" + e.stack); } } } }; TestResults.prototype.verifyExpectedTestsRun = function(expected) { if (this.testsRun != expected) error("Must be " + expected + " tests run, instead ran " + this.testsRun); else r.list(); }; function TestCase() { this.results = undefined; this.name = "TestCase"; } TestCase.prototype.toString = function() { return this.name; } TestCase.prototype.run = function(results_arg) { this.results = results_arg ? results_arg : new TestResults(); this.results.startTest(this); try { this.runTest(); } catch (e) { this.results.addError(this, e); } }; // Build test cases for methods named 'test' in this object. TestCase.prototype.buildTestCases = function() { let ans = new Array(); for (let k in this) { let v = this[k]; if (k.substr(0, 4) == 'test' && typeof v == 'function') { let c = new TestCase(); c.name = k; c.runTest = v; ans.push(c); } } return ans; }; TestCase.prototype.runTestCases = function(results) { let cs = this.buildTestCases() for (let i = 0; i < cs.length; ++i) { cs[i].run(results); } } TestCase.prototype.fail = function(msg) { throw new Error(msg); }; // override this to provide nicer error messages TestCase.prototype.description = function () { return ""; } TestCase.prototype.assertEquals = function(v1, v2) { if (v1 != v2) { throw new Error(this.description() + v1 + ' != ' + v2); } }; TestCase.prototype.assertTrue = function(v) { if (!v) { throw new Error(this.description() + v + ' is false'); } }; dehydra-89ed48e70997/test/unit_test_harness.py0000644000000000000000000002221711757635752021264 0ustar rootroot00000000000000# -*- coding: utf-8 -*- """Test Harness Specify a test by a JS file in this directory with the first line like this: // { 'test': 'dehydra', 'input': 'empty.cc', 'output': 'unit_test', 'lang': 'c', 'args': { 'a': '1', 'b': '2' }, 'version': '4.5' } test: comma-separated list of plugins to test, dehydra or treehydra input: C++ input file to use for test output: eval'd in this script to yield a checker function applied to the output. See checkers, below. lang: comma-separated list of languages to test, c or c++. defaults to c++ args: list of key value pairs passed as arguments to the script version: minimum version of GCC to run test on. """ import os, re, sys from subprocess import Popen, PIPE, STDOUT from unittest import TestCase, TestSuite, TestLoader, TestResult class PluginTestCase(TestCase): """Test case for running Treehydra and checking output.""" def __init__(self, plugin, lang, jsfile, arguments, ccfile, checker, checker_str): super(PluginTestCase, self).__init__() self.plugin = plugin self.lang = lang self.jsfile = jsfile self.arguments = arguments self.ccfile = ccfile self.checker = checker self.checker_str = checker_str def runTest(self): cmd = self.getCommand() sub = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) out, err = sub.communicate() self.checker(self, 0, out, err) def getCommand(self): command = CXX # turn .../g++ into .../gcc if self.lang == 'c': command = re.sub(r'g\+\+([^/]*)$', r'gcc\1', command) elif self.lang == 'c++0x': command += " -std=c++0x" command += " -c -fplugin=../gcc_" + self.plugin + ".so -o /dev/null" command += " -fplugin-arg-gcc_" + self.plugin + "-script=" + self.jsfile argprefix = "-fplugin-arg-gcc_" + self.plugin + "-" for key, value in self.arguments.iteritems(): command += " " + argprefix + key + "=" + value command += " " + self.ccfile return command def __str__(self): return '%-10s %-18s %-16s %s'%( self.plugin, self.jsfile, self.ccfile + ':', self.checker_str) class MyTestResult(TestResult): """The default version stores formatted tracebacks for the failures. We want just the message.""" def addSuccess(self, *args, **kw): super(MyTestResult, self).addSuccess(*args, **kw) self.printResult('pass', args[0]) def addError(self, *args, **kw): super(MyTestResult, self).addError(*args, **kw) self.printResult('error', args[0]) def addFailure(self, test, err): self.failures.append((test, err[1].args)) self.printResult('fail', args[0]) class TinderboxTestResult(MyTestResult): tagMap = { 'pass': 'TEST-PASS', 'error': 'TEST-UNEXPECTED-FAIL (error)', 'fail': 'TEST-UNEXPECTED-FAIL' } def printResult(self, tag, test): print('%s | %s | %s | %s' % (self.tagMap[tag], test.plugin, test.ccfile, test.jsfile)) def finish(self): pass class DevTestResult(MyTestResult): tagMap = { 'pass': '.', 'error': 'e', 'fail': 'x' } def printResult(self, tag, test): sys.stderr.write(self.tagMap[tag]) def finish(self): sys.stderr.write('\n') # Checkers def stderr_has(*args): def checker(test, ec, out, err): for e in args: if err.find(e) == -1: test.fail("Expected '%s' in error output; not found. stderr:%s"%(e,err)) return checker def stderr_has_re(*args): def checker(test, ec, out, err): for e in args: if not re.search(e, err, re.M): test.fail("Expected to find regular expression '%s' in error output; not found. stderr:%s"%(e, err)) return checker def stdout_has(*args): def checker(test, ec, out, err): for e in args: if out.find(e) == -1: test.fail("Expected '%s' in output; not found. stdout:%s"%(e, out)) return checker def stdout_has_re(*args): def checker(test, ec, out, err): for e in args: if not re.search(e, out, re.M): test.fail("Expected to find regular expression '%s' in output; not found. stdout:%s"%(e, out)) return checker def stderr_empty(test, ec, out, err): test.failUnless(err == '', "Expected no error output, got error output :%s" % err) def unit_test(test, ec, out, err): # Get last line lines = out.split('\n') if lines and lines[-1] == '': lines = lines[:-1] msg = "got empty stdout, stderr" if err != '': msg = "Errors:\n %s" % err test.failUnless(lines, "Expected 'OK' output; %s" % msg) last = lines[-1] test.failUnless(last.startswith('OK'), "Expected 'OK' output; got '%s'"%last) def firstLine(filename): """Read the first line of the given file.""" for line in open(filename): return line return '' def extractTests(filename): """Attempt to extract a test case from the given JS file. Return a list of extracted test cases, which may be empty.""" line = firstLine(filename) m = re.match(r'\s*//\s*({.*)', line) if not m: return [] spec = eval(m.group(1)) plugins = spec.get('test') if not plugins: raise TestSpecException(filename, "missing test") plugins = plugins.split(',') for p in plugins: if p not in ('dehydra', 'treehydra'): raise TestSpecException(filename, "invalid plugin %s"%p) srcfile = spec.get('input') if srcfile is None: srcfile = 'empty.cc' checker_str = spec.get('output') if checker_str is None: checker_str = 'unit_test' checker = eval(checker_str) version = spec.get('version') # XXX: check version against gcc version arguments = spec.get('args') if arguments is None: arguments = {} langs = spec.get('lang') if langs is None: langs = 'c,c++' # ignore C tests if C is not enabled supported = set(['c++', 'c++0x']) if 'C_SUPPORT' in config_opts: supported.update('c') langs = langs.split(',') for lang in langs: if lang not in ('c', 'c++', 'c++0x'): raise TestSpecException(filename, "invalid language %s"%lang) return [ PluginTestCase(plugin, lang, filename, arguments, srcfile, checker, checker_str) for plugin in plugins for lang in supported.intersection(langs)] def parseConfigFile(config_filename): config = dict() f = open("../config.mk") for line in f: line = line.strip() if line.startswith("#"): continue fragments = line.split("=") config[fragments[0]] = "=".join(fragments[1:]) f.close() return config from optparse import OptionParser parser = OptionParser() parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='verbose output') parser.add_option('--tinderbox', action='store_true', dest='tinderbox', help='tinderbox-compatible output') OPTIONS, args = parser.parse_args() if len(args) != 2: print "usage: python %s [-v] "%(sys.argv[0]) sys.exit(1) plugin_str = args[0] if plugin_str == 'both': PLUGINS = [ 'dehydra', 'treehydra' ] else: PLUGINS = plugin_str.split(',') # This should be the compiler to use CXX = args[1] # Parse the configuration file config_opts = parseConfigFile("../config.mk") from glob import glob tests = [] # This test can't go in a file, because it tests when the file doesn't exist. for plugin in ("dehydra", "treehydra"): tests.append(PluginTestCase(plugin, 'c++', 'nofile.js', {}, 'empty.cc', stderr_has('Cannot find include file'), '')) # For now, we'll put this here, but if more are created, we should make # the test harness search dirs. for plugin in ("dehydra", "treehydra"): tests.append(PluginTestCase(plugin, 'c++', 'subdir/main.js', {}, 'empty.cc', unit_test, '')) for f in glob('*.js'): try: tests += extractTests(f) except: print "Warning: %s: error extracting tests"%f import traceback traceback.print_exc() if OPTIONS.verbose: print 'Test Cases:' for t in tests: if t.plugin in PLUGINS: tag = 'enabled' else: tag = 'disabled' print " %-10s %s"%(tag, str(t)) s = TestSuite() for t in tests: if t.plugin in PLUGINS: s.addTest(t) if OPTIONS.tinderbox: r = TinderboxTestResult() else: r = DevTestResult() s.run(r) r.finish() if r.wasSuccessful(): print "OK: %d tests passed"%r.testsRun else: for c, tr in r.errors: print "Test Error: Python Exception" print " Test command: %s"%c.getCommand() print " Stack trace:" print tr for c, tr in r.failures: print "Test Failure: %s"%"" print " Test command: %s"%c.getCommand() print " Failure msg: %s"%tr print print "Unit Test Suite Summary:" print " %3d passed"%(r.testsRun - len(r.failures) - len(r.errors)) print " %3d failed"%len(r.failures) print " %3d error(s)"%len(r.errors) sys.exit(1) dehydra-89ed48e70997/test/using_namespace.cc0000644000000000000000000000011211757635752020607 0ustar rootroot00000000000000#include void f() { using namespace std; make_pair(1, 2); } dehydra-89ed48e70997/test/var_templ.cc0000644000000000000000000000012411757635752017442 0ustar rootroot00000000000000template void func(A... args); template class C {}; dehydra-89ed48e70997/test/variant.cc0000644000000000000000000000003611757635752017117 0ustar rootroot00000000000000void f(int *i, const int *j); dehydra-89ed48e70997/test/virtual.cc0000644000000000000000000000021411757635752017137 0ustar rootroot00000000000000class Base { public: virtual int foo() = 0; }; class Der:public Base { public: int foo(); }; void f (Base *d) { int i = d->foo(); } dehydra-89ed48e70997/test/virtual_fn_addr_exprs.cc0000644000000000000000000000305311757635752022041 0ustar rootroot00000000000000class Base { public: virtual void b(); virtual void a(); }; class Derived : public Base { double z; public: virtual void a(); int e(); int x; int y; virtual void b(); virtual void c(); virtual double d(int x); }; class NotDerived { public: virtual void a(int x); virtual void b(double y); virtual int c(); }; typedef decltype(&Base::a) aPtrBase; typedef decltype(&Base::b) bPtrBase; typedef decltype(&Derived::a) aPtrDerived; struct A { aPtrBase ptr; }; struct B { A array[2]; }; A a_arr_glob[] = { { reinterpret_cast(&Derived::a) }, { &Base::a } }; static B b_arr_glob[] = { { { &Base::a, reinterpret_cast(&Derived::a) } } }; aPtrBase a_ptr_glob = &Base::a; aPtrBase b_ptr_glob = reinterpret_cast(&NotDerived::b); void bar(aPtrBase); void foo() { static A a_arr[] = { { reinterpret_cast(&Derived::a) }, { &Base::a } }; A a_arr2[] = { { reinterpret_cast(&Derived::a) }, { &Base::a } }; aPtrBase aptr = reinterpret_cast(&Derived::a); aPtrBase really_b_ptr = reinterpret_cast(&NotDerived::b); static B b_arr[] = { { { &Base::a, reinterpret_cast(&Derived::a) } } }; B b_arr2[] = { { { reinterpret_cast(&Derived::a), &Base::a } } }; static B b_arr3[] = { { { reinterpret_cast(&Derived::d), reinterpret_cast(&Derived::c) } } }; aPtrDerived derived = reinterpret_cast(&NotDerived::b); // In GENERIC but not GIMPLE, this call arg is of type PTRMEM_CST. bar(reinterpret_cast(&NotDerived::c)); } dehydra-89ed48e70997/test/virtual_inheritance.cc0000644000000000000000000000012411757635752021510 0ustar rootroot00000000000000class A {}; class B {}; class C : A, virtual B { }; C* getC() { return 0; } dehydra-89ed48e70997/test/warning.js0000644000000000000000000000023411757635752017147 0ustar rootroot00000000000000function process() { /* see the generated enum opt_c in gcc-build/gcc/options.h */ warning (32, "Here i am warning #32 which is enabled with -Wabi"); } dehydra-89ed48e70997/treehydra.c0000644000000000000000000004113211757635752016322 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include //spidermonkey/gcc conflict #ifdef TEST_BIT #undef TEST_BIT #endif #include #include #include #include #include #include #include #include "cp-tree-jsapi-workaround.h" #include #include #include #include #include #include #include "xassert.h" #include "dehydra_builtins.h" #include "util.h" #include "dehydra.h" #include "dehydra_types.h" #include "treehydra.h" #include "jsval_map.h" // mess to figure out what gimple is implemented like #ifdef GIMPLE_TUPLE_P // 4.3 #define TREEHYDRA_HAS_TSCOMMON(t) (!GIMPLE_TUPLE_P(t)) #else #define TREEHYDRA_HAS_TSCOMMON(t) 1 //gcc vers other than 4.3 #ifndef __APPLE_CC__ // checking for __APPLE_CC__ just checking for 4.2 //4.4 and onwards #include "gimple.h" #define TREEHYDRA_GIMPLE_TUPLES #endif #endif // no precompiler symbol defined for fatvals change (bug 549143) #ifndef JSID_TO_STRING #define JSID_TO_STRING(id) JSVAL_TO_STRING(id) #endif #if JS_VERSION < 180 #error "Need SpiderMonkey 1.8 or higher. Treehydra does not support older spidermonkeys due to lack of JS_AlreadyHasOwnProperty" #endif /* The entries in the map should be transitively rooted by this->globalObj's current_function_decl property */ static struct jsval_map *jsvalMap = NULL; /* this helps correlate js nodes to their C counterparts */ static int global_seq = 0; static JSObject *dehydraSysObj = NULL; int treehydra_debug = 0; typedef struct { treehydra_handler handler; void *data; } lazy_handler; void convert_tree_node_union (struct Dehydra *this, enum tree_node_structure_enum code, union tree_node *var, struct JSObject *obj); #ifdef TREEHYDRA_GIMPLE_TUPLES void convert_gimple_statement_d_union (struct Dehydra *this, unsigned int code, union gimple_statement_d *topmost, struct JSObject *obj); #endif /* delete me */ JSBool tree_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { fprintf(stderr, "tree_construct\n"); return JS_TRUE; } void tree_finalize(JSContext *cx, JSObject *obj) { JS_free(cx, JS_GetPrivate(cx, obj)); } static const char *SEQUENCE_N = "SEQUENCE_N"; /* JavaScript class resolve hook function for Treehydra lazy objects. * The strategy here is to call the lazy handler exactly once, the * first time any property is asked for. Thus, the handler must install * all lazy properties. */ static JSBool ResolveTreeNode (JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) { Dehydra *this = JS_GetContextPrivate (cx); /* when the going gets tough will be able to implement unions using the id field.for now avoiding it at all cost*/ lazy_handler *lazy = JS_GetPrivate (cx, obj); *objp = obj; if (!lazy) { if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_DETECTING)) { return JS_TRUE; } /* The lazy handler has already been called. Standard behavior would * be to let the interpreter to continue searching the scope chain. * Instead, we're going to check it first so that we can return an * error if and only if the property doesn't exist. * * A better way to do this would be to simply set strict mode, but * strict mode doesn't always report this condition (see bug 425066). */ JSBool has_prop; JSObject *protoObj = JS_GetPrototype(cx, obj); JSString *str = JSID_TO_STRING(id); char *prop_name = JS_EncodeString(cx, str); xassert(prop_name); JSBool rv = JS_HasProperty(cx, protoObj, prop_name, &has_prop); if (rv && has_prop) { *objp = protoObj; } else { /* Property not found anywhere: produce the error. */ jsval unhandled_property_handler = dehydra_getToplevelFunction( this, "unhandledLazyProperty"); jsval rval; jsval arg = STRING_TO_JSVAL(str); rv = JS_CallFunctionValue(this->cx, this->globalObj, unhandled_property_handler, 1, &arg, &rval); } JS_free(cx, prop_name); return rv; } JS_SetPrivate (cx, obj, NULL); lazy->handler (this, lazy->data, obj); free (lazy); return JS_TRUE; } static JSClass js_tree_class = { "GCCNode", /* name */ JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, /* flags */ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,(JSResolveOp) ResolveTreeNode, JS_ConvertStub, tree_finalize, NULL, NULL, NULL, tree_construct, NULL, NULL, NULL, NULL }; /* setups a lazy object. transitively rooted through parent */ jsval get_lazy (Dehydra *this, treehydra_handler handler, void *v, JSObject *parent, const char *propname) { JSObject *obj; jsval jsvalObj; xassert (parent && propname); obj = definePropertyObject( this->cx, parent, propname, &js_tree_class, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); jsvalObj = OBJECT_TO_JSVAL (obj); lazy_handler *lazy = xmalloc (sizeof (lazy_handler)); lazy->handler = handler; lazy->data = v; JS_SetPrivate (this->cx, obj, lazy); return jsvalObj; } /* This either returnes a cached object or creates a new lazy object */ jsval get_existing_or_lazy (Dehydra *this, treehydra_handler handler, void *v, JSObject *parent, const char *propname) { if (!v) { dehydra_defineProperty (this, parent, propname, JSVAL_VOID); return JSVAL_VOID; } jsval val; bool found = jsval_map_get (jsvalMap, v, &val); if (found) { dehydra_defineProperty (this, parent, propname, val); return val; } const jsval jsret = get_lazy (this, handler, v, parent, propname); jsval_map_put(jsvalMap, v, jsret); return jsret; } #ifdef TREEHYDRA_GIMPLE_TUPLES // I would love to implement this as a toplevel gimple_op(gs,i) func // but currently there is no robust way to go from a js object to the C(once it gets delazified) void lazy_gimple_ops (Dehydra *this, void *structure, JSObject *obj) { gimple stmt = (gimple)structure; char buf[32]; unsigned i; for (i = 0; i < gimple_num_ops (stmt); i++) { sprintf (buf, "%d", i); get_existing_or_lazy (this, lazy_tree_node, gimple_op (stmt, i), obj, buf); } } // this kind of crap should be generated by convert_tree.js void lazy_gimple_statement_d (Dehydra *this, void *structure, JSObject *obj) { gimple stmt = (gimple)structure; convert_gimple_statement_d_union (this, GSS_BASE, stmt, obj); convert_gimple_statement_d_union (this, gimple_statement_structure(stmt), stmt, obj); get_lazy (this, lazy_gimple_ops, stmt, obj, "gimple_ops"); } #endif void lazy_tree_string (struct Dehydra *this, void* void_var, struct JSObject *obj) { int wchar_bytes; int num_chars; struct tree_string *topmost = (struct tree_string*) void_var; tree str = (tree) void_var; if (!void_var) return; // reflect stuff in the struct as treehydra_generated would've get_lazy (this, lazy_tree_common, &topmost->common, obj, "common"); convert_int(this, obj, "length", (HOST_WIDE_INT) topmost->length); // now reflect .str, account for unicode magic (bug 526970) tree str_type = TREE_TYPE (str); if (str_type && TYPE_PRECISION (TREE_TYPE (str_type)) == TYPE_PRECISION (char_type_node)) { wchar_bytes = 1; } else { wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT; } num_chars = (TREE_STRING_LENGTH (str) / wchar_bytes); // TREE_STRING_LENGTH is 0 for certain empty strings if (num_chars != 0) { // skip trailing null --num_chars; } if (wchar_bytes == 1) { jsval v = STRING_TO_JSVAL (JS_NewStringCopyN (this->cx, TREE_STRING_POINTER (str), num_chars)); dehydra_defineProperty (this, obj, "str", v); } else { int i; jsval v; jschar *buf = xmalloc (num_chars * sizeof(jschar)); for (i = 0; i < num_chars; ++i) { if (wchar_bytes == 2) buf[i] = ( (const uint16_t*)(TREE_STRING_POINTER (str)) )[i]; else // assume wchar_bytes == 4 buf[i] = ( (const uint32_t*)(TREE_STRING_POINTER (str)) )[i]; // (this assumes something about endianness) } v = STRING_TO_JSVAL (JS_NewUCStringCopyN (this->cx, buf, num_chars)); dehydra_defineProperty (this, obj, "str", v); free (buf); } } // work around the fact that DECL_ASSEMBLER_NAME needs to do a callback if the name isn't initialized yet // TODO: needs testcase void lazy_decl_as_string (Dehydra *this, void *structure, JSObject *obj) { tree t = (tree) structure; convert_char_star(this, obj, "str", decl_as_string(t, 0)); } // this kind of crap should be generated by convert_tree.js void lazy_decl_assembler_name (Dehydra *this, void *structure, JSObject *obj) { tree t = (tree) structure; get_lazy (this, lazy_tree_node, HAS_DECL_ASSEMBLER_NAME_P(t) ? DECL_ASSEMBLER_NAME(t) : NULL_TREE, obj, "identifier"); } /* This code is ugly because it deals with low level representation * if gcc's tree union */ void lazy_tree_node (Dehydra *this, void *structure, JSObject *obj) { tree t = (tree)structure; if (treehydra_debug) { const int myseq = ++global_seq; dehydra_defineProperty (this, obj, SEQUENCE_N, INT_TO_JSVAL (myseq)); } enum tree_code code = TREE_CODE (t); /* special case knowledge of non-decl nodes */ #ifndef __APPLE_CC__ // no TS_BASE in mac gcc 42 and no easy way to check too convert_tree_node_union (this, TS_BASE, t, obj); #endif if (TREEHYDRA_HAS_TSCOMMON (t)) { convert_tree_node_union (this, TS_COMMON, t, obj); } /* do not do tree_node_structure() for non C types */ if (code < NUM_TREE_CODES || (isGPlusPlus() && cp_tree_node_structure ((union lang_tree_node *)t) == TS_CP_GENERIC)) { enum tree_node_structure_enum i = tree_node_structure (t); convert_tree_node_union (this, i, t, obj); /* stuff below is empty for non-decls */ if (!DECL_P (t)) return; get_lazy (this, lazy_decl_as_string, t, obj, "_decl_as_string"); get_lazy (this, lazy_decl_assembler_name, t, obj, "_decl_assembler_name"); for (i = 0; i < LAST_TS_ENUM;i++) { if (tree_contains_struct[code][i]) { convert_tree_node_union (this, i, t, obj); } } } else { // gaurd is tripped by TEMPLATE_INFO, TEMPLATE_PARM_INDEX, and OVERLOAD nodes //fprintf(stderr, "%s\n", tree_code_name[code]); } } /* BEGIN Functions for treehydra_generated */ jsval get_enum_value (struct Dehydra *this, const char *name) { jsval val = JSVAL_VOID; JS_GetProperty(this->cx, this->globalObj, name, &val); if (val == JSVAL_VOID) { error ("EnumValue '%s' not found. enums.js isn't loaded", name); } return val; } void convert_char_star (struct Dehydra *this, struct JSObject *parent, const char *propname, const char *str) { jsval v = STRING_TO_JSVAL (JS_NewStringCopyZ (this->cx, str)); dehydra_defineProperty (this, parent, propname, v); } void convert_int (struct Dehydra *this, struct JSObject *parent, const char *propname, HOST_WIDE_INT i) { jsval v; JS_NewNumberValue(this->cx, (jsdouble) i, &v); dehydra_defineProperty (this, parent, propname, v); /* because 64-bit ints can overflow a jsdouble, do the same representation, unsigned, in string form */ static char buf[32]; sprintf(buf, "%llx", (unsigned long long) i); int len = strlen(propname); char *unsignedprop = xmalloc(len + 5); strcpy(unsignedprop, propname); strcpy(unsignedprop + len, "_str"); dehydra_defineStringProperty (this, parent, unsignedprop, buf); free(unsignedprop); } /* END Functions for treehydra_generated */ typedef struct { size_t capacity; size_t length; Dehydra *this; char str[1]; } GrowingString; static tree walk_n_test (tree *tp, int *walk_subtrees, void *data) { enum tree_code code = TREE_CODE (*tp); const char *strcode = tree_code_name[code]; GrowingString **gstr = (GrowingString**) data; /* figure out corresponding seq# by peeking at js */ jsval v; bool found = jsval_map_get (jsvalMap, *tp, &v); int seq = 0; if (found) { JSObject *obj = JSVAL_TO_OBJECT (v); jsval val = JSVAL_VOID; JS_GetProperty(gstr[0]->this->cx, obj, SEQUENCE_N, &val); if (val != JSVAL_VOID) seq = JSVAL_TO_INT (val); } /* 30 is a "this should be big enough" size to fit in [space]+pointers */ while (gstr[0]->length + 30 + strlen(strcode) > gstr[0]->capacity) { gstr[0]->capacity *= 2; gstr[0] = xrealloc (gstr[0], gstr[0]->capacity + sizeof(GrowingString)); } /* point at the end of the string*/ gstr[0]->length += sprintf(gstr[0]->str + gstr[0]->length, "%s %d\n", strcode, seq); return NULL_TREE; } /* Returns a list of walked nodes in the current function body */ JSBool JS_C_walk_tree(JSContext *cx, uintN argc, jsval *vp) { Dehydra *this = JS_GetContextPrivate (cx); const size_t capacity = 512; GrowingString *gstr = xrealloc (NULL, capacity); gstr->capacity = capacity - sizeof(GrowingString); gstr->this = this; gstr->length = 0; gstr->str[0] = 0; tree body_chain = DECL_SAVED_TREE (current_function_decl); struct pointer_set_t *pset = pointer_set_create (); walk_tree (&body_chain, walk_n_test, &gstr, pset); pointer_set_destroy (pset); /* remove last \n */ if (gstr->length) gstr->str[gstr->length - 1] = 0; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL (JS_NewStringCopyZ (this->cx, gstr->str))); free(gstr); return JS_TRUE; } static void lazy_integer_types (Dehydra *this, void *structure, JSObject *obj) { int i; char buf[8]; for (i = 0;i < sizeof(integer_types)/sizeof(integer_types[0]);++i) { sprintf(buf, "%d", i); get_existing_or_lazy (this, lazy_tree_node, integer_types[i], obj, buf); } } static void lazy_gcc_globals (Dehydra *this, void *structure, JSObject *obj) { get_existing_or_lazy (this, lazy_cgraph_node, cgraph_nodes, obj, "cgraph_nodes"); get_lazy (this, lazy_integer_types, NULL, obj, "integer_types"); } static void lazy_treehydra_globals (Dehydra *this, void *structure, JSObject *obj) { get_lazy (this, lazy_gcc_globals, NULL, obj, "gcc"); } void treehydra_call_js (struct Dehydra *this, const char *callback, tree treeval) { jsval process = dehydra_getToplevelFunction(this, callback); if (process == JSVAL_VOID) return; jsval rval; xassert (!jsvalMap); jsvalMap = jsval_map_create(); get_lazy (this, lazy_treehydra_globals, NULL, dehydraSysObj, "treehydra"); // Ensure that error/warning report errors within a useful context tree old_current_function_decl = NULL_TREE; if (current_function_decl != treeval && TREE_CODE (treeval) == FUNCTION_DECL) { old_current_function_decl = current_function_decl; current_function_decl = treeval; } jsval fnval = get_existing_or_lazy (this, lazy_tree_node, treeval, this->globalObj, "__treehydra_top_obj"); if (old_current_function_decl) current_function_decl = old_current_function_decl; xassert (JS_CallFunctionValue (this->cx, this->globalObj, process, 1, &fnval, &rval)); JS_DeleteProperty (this->cx, dehydraSysObj, "treehydra"); JS_DeleteProperty (this->cx, this->globalObj, "__treehydra_top_obj"); jsval_map_destroy(jsvalMap); jsvalMap = NULL; JS_MaybeGC(this->cx); } int treehydra_startup (Dehydra *this) { jsval sys_val = JSVAL_VOID; JS_GetProperty(this->cx, this->globalObj, SYS, &sys_val); xassert (sys_val != JSVAL_VOID); dehydraSysObj = JSVAL_TO_OBJECT (sys_val); xassert (JS_DefineFunction (this->cx, this->globalObj, "C_walk_tree", (JSNative) JS_C_walk_tree, 0, JSFUN_FAST_NATIVE)); xassert (JS_InitClass(this->cx, this->globalObj, NULL ,&js_tree_class , NULL, 0, NULL, NULL, NULL, NULL)); xassert (!dehydra_includeScript (this, "treehydra.js")); return 0; } dehydra-89ed48e70997/treehydra.h0000644000000000000000000000505511757635752016333 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TREEHYDRA_H #define TREEHYDRA_H int treehydra_startup (struct Dehydra *this); int set_after_gcc_pass(const char *pass); typedef void (*treehydra_handler)(struct Dehydra *this, void *structure, struct JSObject *obj); jsval get_lazy (struct Dehydra *this, treehydra_handler handler, void *v, struct JSObject *parent, const char *propname); jsval get_existing_or_lazy (struct Dehydra *this, treehydra_handler handler, void *v, struct JSObject *parent, const char *propname); jsval get_enum_value (struct Dehydra *this, const char *name); void lazy_tree_node (struct Dehydra *this, void *structure, struct JSObject *obj); // these are generated code called from treehydra.c void lazy_cgraph_node (struct Dehydra *this, void* void_var, struct JSObject *obj); void lazy_tree_common (struct Dehydra *this, void* void_var, struct JSObject *obj); // This stuff became important in gcc 4.5 void lazy_gimple_statement_d (struct Dehydra *this, void* void_var, struct JSObject *obj); struct JSObject *dehydra_defineArrayProperty (struct Dehydra *this, struct JSObject *obj, char const *name, int length); void convert_int (struct Dehydra *this, struct JSObject *parent, const char *propname, HOST_WIDE_INT i); void convert_char_star (struct Dehydra *this, struct JSObject *parent, const char *propname, const char *str); void treehydra_call_js (struct Dehydra *this, const char *callback, union tree_node *treeval); extern int treehydra_debug; #endif dehydra-89ed48e70997/treehydra_builtins.c0000644000000000000000000000167511757635752020243 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define TREEHYDRA_PLUGIN #include "dehydra_builtins.c" dehydra-89ed48e70997/treehydra_generated.h0000644000000000000000000000405011757635752020343 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef TREEHYDRA_GENERATED #define TREEHYDRA_GENERATED /* This file isn't generated, but it's used by the generated one*/ #include "gcc_cp_headers.h" #include "gcc_compat.h" #include // cleanup some gcc polution in GCC trunk as of Mar 5, 2008 #ifdef in_function_try_handler #undef in_function_try_handler #endif #ifdef in_base_initializer #undef in_base_initializer #endif #ifdef profile_status #undef profile_status #endif struct JSObject; typedef uint64_t jsval; struct Dehydra; void dehydra_defineProperty(struct Dehydra *this, struct JSObject *obj, char const *name, jsval value); void dehydra_defineStringProperty(struct Dehydra *this, struct JSObject *obj, char const *name, char const *value); struct JSObject *dehydra_defineObjectProperty (struct Dehydra *this, struct JSObject *obj, char const *name); void convert_location_t (struct Dehydra *this, struct JSObject *parent, const char *propname, location_t loc); void lazy_tree_string (struct Dehydra *this, void* void_var, struct JSObject *obj); #include "treehydra.h" #endif dehydra-89ed48e70997/treehydra_plugin.c0000644000000000000000000000167211757635752017705 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define TREEHYDRA_PLUGIN #include "dehydra_plugin.c" dehydra-89ed48e70997/useful_arrays.c0000644000000000000000000000252211757635752017217 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE, #define END_OF_BASE_TREE_CODES const enum tree_code_class tree_code_type[] = { #include "all-tree.def" }; #undef DEFTREECODE #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH, const unsigned char tree_code_length[] = { #include "all-tree.def" }; #undef DEFTREECODE #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) NAME, const unsigned char tree_code_name[] = { #include "all-tree.def" }; #undef DEFTREECODE dehydra-89ed48e70997/util.c0000644000000000000000000001165311757635752015315 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "gcc_compat.h" #include "xassert.h" #include "util.h" #include #include static char locationbuf[PATH_MAX]; /* stolen from gcc/cp/error.h */ location_t location_of (tree t) { if (TREE_CODE (t) == PARM_DECL && DECL_CONTEXT (t)) t = DECL_CONTEXT (t); else if (TYPE_P (t)) t = TYPE_MAIN_DECL (t); else if (TREE_CODE (t) == OVERLOAD) t = OVL_FUNCTION (t); if (!t) return UNKNOWN_LOCATION; if (DECL_P(t)) return DECL_SOURCE_LOCATION (t); else if (EXPR_P(t) && EXPR_HAS_LOCATION(t)) return EXPR_LOCATION(t); else return UNKNOWN_LOCATION; } char const * loc_as_string (location_t loc) { if (loc_is_unknown(loc)) return NULL; expanded_location eloc = expand_location(loc); #ifdef __APPLE__ // XXX remove once Apple GCC supports columns sprintf(locationbuf, "%s:%d", eloc.file, eloc.line); #else // a function, so locations are likely to be mapped, so we have columns sprintf(locationbuf, "%s:%d:%d", eloc.file, eloc.line, eloc.column); #endif return locationbuf; } /* this is to idicate that global_namespace isn't linked in */ tree global_namespace = NULL; bool isGPlusPlus() { return cp_walk_subtrees != NULL; } const char * class_key_or_enum_as_string (tree t) { if (TREE_CODE (t) == ENUMERAL_TYPE) return "enum"; else if (TREE_CODE (t) == UNION_TYPE) return "union"; else if (isGPlusPlus() && TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t)) return "class"; else return "struct"; } const char * decl_as_string (tree decl, int flags) { tree aname = DECL_P (decl) ? DECL_NAME (decl) : decl; return aname ? IDENTIFIER_POINTER (aname) : ""; } const char * expr_as_string (tree decl, int flags) { static char buf[256]; if (!decl) return ""; else if (TREE_CODE (decl) == INTEGER_CST) { return dehydra_intCstToString(decl); } else { sprintf(buf, "?%s?", tree_code_name[TREE_CODE(decl)]); } return buf; } const char * type_as_string (tree typ, int flags) { return decl_as_string (TYPE_NAME(typ), 0); } void lang_check_failed (const char* file, int line, const char* function) { internal_error ("lang_* check: failed in %s, at %s:%d", function, trim_filename (file), line); } /* Convert an INTEGER_CST to a string representation. This is used * because GCC expr_as_string is broken for unsigned ints. */ const char *dehydra_intCstToString(tree int_cst) { static char buf[32]; // holds repr of up to 64-bit ints xassert(TREE_CODE(int_cst) == INTEGER_CST); tree type = TREE_TYPE(int_cst); int is_unsigned = TYPE_UNSIGNED(type); #if HOST_BITS_PER_WIDE_INT > 32 // TREE_INT_CST_LOW(int_cst) is a 64-bit integer here sprintf(buf, is_unsigned ? "%"PRIu64"u" : "%"PRId64, TREE_INT_CST_LOW(int_cst)); #else int high = TREE_INT_CST_HIGH(int_cst); int low = TREE_INT_CST_LOW(int_cst); if (high == 0 || (high == -1 && !is_unsigned)) { /* GCC prints negative signed numbers in hex, we print using %d. GCC prints unsigned numbers as if signed, we really do unsigned. */ sprintf(buf, is_unsigned ? "%uu" : "%d", low); } else { /* GCC prints negative 64-bit constants in hex, we want %d. GCC prints large positive unsigned 64-bit constants in hex, we want %u */ sprintf(buf, is_unsigned ? "%lluu" : "%lld", ((long long)high << 32) | (0xffffffffll & low)); } #endif if (type == long_integer_type_node || type == long_unsigned_type_node) strcat(buf, "l"); else if (type == long_long_integer_type_node || type == long_long_unsigned_type_node) strcat(buf, "ll"); return buf; } // compatability shim enum cp_tree_node_structure_enum cp_tree_node_structure (union lang_tree_node *_) { xassert(false); return LAST_TS_ENUM; } tree * decl_cloned_function_p (const_tree decl, bool just_testing) { xassert (0); } void crashhandler() { void* callstack[128]; int size = backtrace(callstack, sizeof(callstack)/sizeof(callstack[0])); backtrace_symbols_fd(callstack, size, 2); fprintf(stderr, "Sleeping 60 seconds, pid is %d\n", getpid()); sleep(60); _exit(1); } dehydra-89ed48e70997/util.h0000644000000000000000000000226711757635752015323 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef UTIL_H #define UTIL_H location_t location_of (tree t); static inline bool loc_is_unknown(location_t loc) { location_t unk = UNKNOWN_LOCATION; return !memcmp(&loc, &unk, sizeof(location_t)); } char const * loc_as_string (location_t loc); const char* dehydra_intCstToString(tree int_cst); #endif dehydra-89ed48e70997/xassert.h0000644000000000000000000000303111757635752016025 0ustar rootroot00000000000000/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Dehydra and Treehydra scriptable static analysis tools * Copyright (C) 2007-2010 The Mozilla Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef XASSERT_H #define XASSERT_H void crashhandler(); #define xassert(cond) \ if (!(cond)) { \ fprintf(stderr, "%s:%d: Assertion failed:" #cond \ ". \nIf the file compiles correctly without invoking " \ "dehydra please file a bug, include a testcase or .ii file" \ " produced with -save-temps\n", \ __FILE__, __LINE__); \ crashhandler(); \ } #endif